cellular 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -2
  3. data/cellular.gemspec +5 -5
  4. data/lib/cellular.rb +1 -1
  5. data/lib/cellular/backends.rb +5 -3
  6. data/lib/cellular/backends/backend.rb +3 -2
  7. data/lib/cellular/backends/cool_sms.rb +3 -4
  8. data/lib/cellular/backends/link_mobility.rb +72 -0
  9. data/lib/cellular/backends/log.rb +1 -0
  10. data/lib/cellular/backends/sendega.rb +12 -13
  11. data/lib/cellular/backends/test.rb +1 -0
  12. data/lib/cellular/backends/twilio.rb +4 -4
  13. data/lib/cellular/configuration.rb +3 -6
  14. data/lib/cellular/jobs.rb +2 -1
  15. data/lib/cellular/jobs/async_messenger.rb +1 -0
  16. data/lib/cellular/logger.rb +2 -5
  17. data/lib/cellular/models/sms.rb +7 -5
  18. data/lib/cellular/version.rb +1 -1
  19. data/spec/cellular/backends/backend_spec.rb +19 -0
  20. data/spec/cellular/backends/cool_sms_spec.rb +31 -27
  21. data/spec/cellular/backends/link_mobility_spec.rb +118 -0
  22. data/spec/cellular/backends/log_spec.rb +3 -5
  23. data/spec/cellular/backends/log_with_rails_spec.rb +5 -7
  24. data/spec/cellular/backends/sendega_spec.rb +35 -37
  25. data/spec/cellular/backends/test_spec.rb +1 -12
  26. data/spec/cellular/backends/twilio_spec.rb +47 -42
  27. data/spec/cellular/configuration_spec.rb +1 -3
  28. data/spec/cellular/jobs/async_messenger_spec.rb +4 -4
  29. data/spec/cellular/logger_spec.rb +0 -2
  30. data/spec/cellular/models/sms_spec.rb +15 -15
  31. data/spec/cellular_spec.rb +0 -2
  32. data/spec/fixtures/backends/link_mobility/failure.json +1 -0
  33. data/spec/fixtures/backends/link_mobility/success.json +1 -0
  34. data/spec/spec_helper.rb +0 -2
  35. metadata +17 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a97fa1c32bf2c1aea85c36bd15bb489b9aaa20d9
4
- data.tar.gz: a3190fd32b63a7cf284228d0b37481429ce30da4
3
+ metadata.gz: 798ef5b207911f8698d8199cc06900afd9304152
4
+ data.tar.gz: 4854425f3b5a9348c2e42f1786991a4887c3f0a5
5
5
  SHA512:
6
- metadata.gz: 359c6728df2bd17dafc5be5d0f04704ab7a394084ee3e0ace41f112ef48733c9883cd4ad08654a83c03d83a15d06b1fa43e215afb61d3fe628cf8f07118f310c
7
- data.tar.gz: 610ac72f15d403b628dd60cdd70ffcb301dea9297402ec6c0d6c00ec0edfae128bfd962837c14993bffcf1dc95a0c1e6d6295772ae57c32682603f0c174e541c
6
+ metadata.gz: c902309c6f7ec95b0bc5dd478f87fbcceb8cce9a8046e79a1318ee757d978cd19d47e54507eca6bebcf420c98f31ad16756f4da602674133ab2de041ff981ea8
7
+ data.tar.gz: bb89b59775574a107503149b0a0a089d493b7157e77050a41f4413b232ab0c8867c1b3bcdeee5884b49b9be64ff3cbc8c6a4198cab0d30d28f4085d97259c0ed
@@ -1,7 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.0.0
4
- - 2.1.0
5
3
  - 2.2.2
6
4
  - 2.3.0
7
5
  script: bundle exec rake spec
@@ -10,18 +10,18 @@ Gem::Specification.new do |gem|
10
10
  gem.version = Cellular::VERSION
11
11
  gem.authors = ['Sindre Moen', 'Tim Kurvers']
12
12
  gem.email = ['ruby@hyper.no']
13
- gem.description = %q{Sending and receiving SMSs through pluggable backends}
14
- gem.summary = %q{Sending and receiving SMSs through pluggable backends}
13
+ gem.description = 'Sending and receiving SMSs through pluggable backends'
14
+ gem.summary = 'Sending and receiving SMSs through pluggable backends'
15
15
  gem.homepage = ''
16
16
 
17
17
  gem.files = `git ls-files`.split($/)
18
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
19
19
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
20
  gem.require_paths = ['lib']
21
21
 
22
- gem.add_dependency 'httparty', '~> 0.13.0'
22
+ gem.add_dependency 'httparty', '~> 0.13'
23
23
  gem.add_dependency 'savon', '~> 2.0'
24
- gem.add_dependency 'rails', '>= 4.2'
24
+ gem.add_dependency 'rails', '~> 4.2'
25
25
 
26
26
  gem.add_development_dependency 'pry', '~> 0.10'
27
27
 
@@ -4,6 +4,7 @@ require 'cellular/models/sms'
4
4
  require 'cellular/backends'
5
5
  require 'cellular/jobs'
6
6
 
7
+ # Sending and receiving SMSs through pluggable backends
7
8
  module Cellular
8
9
  autoload :Logger, 'cellular/logger'
9
10
 
@@ -21,5 +22,4 @@ module Cellular
21
22
  @config ||= Configuration.new
22
23
  end
23
24
  end
24
-
25
25
  end
@@ -1,10 +1,12 @@
1
1
  module Cellular
2
+ # Encapsulates all available backends for Cellular
2
3
  module Backends
3
4
  autoload :Backend, 'cellular/backends/backend'
4
5
  autoload :CoolSMS, 'cellular/backends/cool_sms'
5
6
  autoload :Sendega, 'cellular/backends/sendega'
6
- autoload :Twilio, 'cellular/backends/twilio'
7
- autoload :Log, 'cellular/backends/log'
8
- autoload :Test, 'cellular/backends/test'
7
+ autoload :Twilio, 'cellular/backends/twilio'
8
+ autoload :LinkMobility, 'cellular/backends/link_mobility'
9
+ autoload :Log, 'cellular/backends/log'
10
+ autoload :Test, 'cellular/backends/test'
9
11
  end
10
12
  end
@@ -1,11 +1,12 @@
1
1
  module Cellular
2
2
  module Backends
3
+ # Base class for a Cellular backend
3
4
  class Backend
4
- def self.deliver(options = {})
5
+ def self.deliver(_options = {})
5
6
  raise NotImplementedError
6
7
  end
7
8
 
8
- def self.receive(data)
9
+ def self.receive(_data)
9
10
  raise NotImplementedError
10
11
  end
11
12
  end
@@ -2,17 +2,17 @@ require 'httparty'
2
2
 
3
3
  module Cellular
4
4
  module Backends
5
+ # Cool SMS backend (http://www.coolsms.com)
5
6
  class CoolSMS < Backend
6
-
7
7
  # Documentation: http://www.coolsms.com/support/dokumentation/http-gateway.sms
8
- GATEWAY_URL = 'https://sms.coolsmsc.dk/'
8
+ GATEWAY_URL = 'https://sms.coolsmsc.dk/'.freeze
9
9
 
10
10
  def self.deliver(options = {})
11
11
  request_queue = {}
12
12
 
13
13
  recipients_batch(options).each_with_index do |recipient, index|
14
14
  options[:batch] = recipient
15
- result = HTTParty.get(GATEWAY_URL, query: defaults_with(options) )
15
+ result = HTTParty.get(GATEWAY_URL, query: defaults_with(options))
16
16
  request_queue[index] = {
17
17
  recipient: recipient,
18
18
  response: parse_response(result.parsed_response['smsc'])
@@ -55,7 +55,6 @@ module Cellular
55
55
  options[:recipients]
56
56
  end
57
57
  end
58
-
59
58
  end
60
59
  end
61
60
  end
@@ -0,0 +1,72 @@
1
+ require 'httparty'
2
+
3
+ module Cellular
4
+ module Backends
5
+ # LinkMobility backend (https://www.linkmobility.com)
6
+ class LinkMobility < Backend
7
+ # Documentation: https://www.linkmobility.com/developers/
8
+ BASE_URL = 'https://wsx.sp247.net/'.freeze
9
+
10
+ HTTP_HEADERS = {
11
+ 'Content-Type' => 'application/json; charset=utf-8',
12
+ }.freeze
13
+
14
+ def self.deliver(options = {})
15
+ request_queue = {}
16
+ recipients_batch(options).each_with_index do |recipient, index|
17
+ options[:batch] = recipient
18
+
19
+ request = HTTParty.post(
20
+ sms_url,
21
+ body: payload(options),
22
+ basic_auth: link_mobility_config,
23
+ headers: HTTP_HEADERS
24
+ )
25
+
26
+ request_queue[index] = {
27
+ recipient: options[:batch],
28
+ response: parse_response(request)
29
+ }
30
+ end
31
+
32
+ # return first response for now
33
+ request_queue[0][:response]
34
+ end
35
+
36
+ def self.parse_response(response)
37
+ [
38
+ response['description']
39
+ ]
40
+ end
41
+
42
+ def self.sms_url
43
+ "#{BASE_URL}sms/send"
44
+ end
45
+
46
+ def self.link_mobility_config
47
+ {
48
+ username: Cellular.config.username,
49
+ password: Cellular.config.password
50
+ }
51
+ end
52
+
53
+ def self.payload(options)
54
+ {
55
+ source: options[:sender],
56
+ destination: options[:batch],
57
+ userData: options[:message],
58
+ platformId: 'COMMON_API',
59
+ platformPartnerId: Cellular.config.partner_id,
60
+ }.to_json
61
+ end
62
+
63
+ def self.recipients_batch(options)
64
+ if options[:recipients].blank?
65
+ [options[:recipient]]
66
+ else
67
+ options[:recipients]
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,5 +1,6 @@
1
1
  module Cellular
2
2
  module Backends
3
+ # Writes SMS messages to configured logger
3
4
  class Log < Backend
4
5
  def self.deliver(options = {})
5
6
  Cellular.config.logger.info options[:message]
@@ -2,9 +2,9 @@ require 'savon'
2
2
 
3
3
  module Cellular
4
4
  module Backends
5
+ # Sendega backend (http://sendega.com)
5
6
  class Sendega < Backend
6
-
7
- GATEWAY_URL = 'https://smsc.sendega.com/Content.asmx?WSDL'
7
+ GATEWAY_URL = 'https://smsc.sendega.com/Content.asmx?WSDL'.freeze
8
8
 
9
9
  def self.deliver(options = {}, savon_options = {})
10
10
  # Send an SMS and return its initial delivery status code.
@@ -45,7 +45,7 @@ module Cellular
45
45
  request_queue[index] = {
46
46
  batch: batch,
47
47
  result: result,
48
- body:result.body[:send_response][:send_result],
48
+ body: result.body[:send_response][:send_result],
49
49
  response: map_response(result.body[:send_response][:send_result])
50
50
  }
51
51
  end
@@ -54,20 +54,20 @@ module Cellular
54
54
  request_queue[0][:response]
55
55
  end
56
56
 
57
- def self.receive(data)
57
+ def self.receive(_data)
58
58
  raise NotImplementedError
59
59
  end
60
60
 
61
61
  def self.savon_config
62
62
  {
63
- username: Cellular.config.username,
64
- password: Cellular.config.password,
65
- dlrUrl: Cellular.config.delivery_url
63
+ username: Cellular.config.username,
64
+ password: Cellular.config.password,
65
+ dlrUrl: Cellular.config.delivery_url
66
66
  }
67
67
  end
68
68
 
69
69
  def self.defaults_with(options)
70
- {
70
+ {
71
71
  sender: options[:sender],
72
72
  destination: options[:batch],
73
73
  pricegroup: options[:price] || 0, # default price to 0
@@ -85,9 +85,9 @@ module Cellular
85
85
  }.merge!(savon_config)
86
86
  end
87
87
 
88
- def self.map_response(_body)
89
- msg = _body[:success] ? success_message : _body[:error_message]
90
- [ _body[:error_number].to_i, msg ]
88
+ def self.map_response(body)
89
+ msg = body[:success] ? success_message : body[:error_message]
90
+ [body[:error_number].to_i, msg]
91
91
  end
92
92
 
93
93
  def self.success_message
@@ -98,10 +98,9 @@ module Cellular
98
98
  if options[:recipients].blank?
99
99
  [options[:recipient]]
100
100
  else
101
- options[:recipients].each_slice(100).to_a.map{|x| x.join(',') }
101
+ options[:recipients].each_slice(100).to_a.map { |x| x.join(',') }
102
102
  end
103
103
  end
104
-
105
104
  end
106
105
  end
107
106
  end
@@ -1,5 +1,6 @@
1
1
  module Cellular
2
2
  module Backends
3
+ # Test backend appends deliveries to Cellular.deliveries
3
4
  class Test < Backend
4
5
  def self.deliver(options = {})
5
6
  Cellular.deliveries << options[:message]
@@ -2,10 +2,11 @@ require 'httparty'
2
2
 
3
3
  module Cellular
4
4
  module Backends
5
+ # Twilio backend (https://www.twilio.com)
5
6
  class Twilio < Backend
6
7
  # Documentation: https://www.twilio.com/docs/api/rest
7
- API_VERSION = '2010-04-01'
8
- BASE_URL = 'https://api.twilio.com/'
8
+ API_VERSION = '2010-04-01'.freeze
9
+ BASE_URL = 'https://api.twilio.com/'.freeze
9
10
  API_URL = BASE_URL + API_VERSION
10
11
 
11
12
  HTTP_HEADERS = {
@@ -14,7 +15,7 @@ module Cellular
14
15
  'User-Agent' => "cellular/#{Cellular::VERSION}" \
15
16
  " (#{RUBY_ENGINE}/#{RUBY_PLATFORM}" \
16
17
  " #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
17
- }
18
+ }.freeze
18
19
 
19
20
  def self.deliver(options = {})
20
21
  request_queue = {}
@@ -71,7 +72,6 @@ module Cellular
71
72
  options[:recipients]
72
73
  end
73
74
  end
74
-
75
75
  end
76
76
  end
77
77
  end
@@ -1,15 +1,12 @@
1
1
  module Cellular
2
+ # Configuration for Cellular
2
3
  class Configuration
3
-
4
4
  attr_accessor :username, :password, :delivery_url, :backend
5
-
6
- attr_accessor :country_code, :price, :sender
7
-
5
+ attr_accessor :country_code, :price, :sender, :partner_id
8
6
  attr_accessor :logger
9
-
7
+
10
8
  def logger
11
9
  @logger ||= Object.const_defined?(:Rails) ? Rails.logger : Logger.new
12
10
  end
13
-
14
11
  end
15
12
  end
@@ -1,9 +1,10 @@
1
1
  begin
2
- require "sidekiq"
2
+ require 'sidekiq'
3
3
  rescue LoadError
4
4
  end
5
5
 
6
6
  module Cellular
7
+ # Encapsulates all background job implementations
7
8
  module Jobs
8
9
  require 'cellular/jobs/async_messenger'
9
10
  end
@@ -1,5 +1,6 @@
1
1
  require 'active_job'
2
2
 
3
+ # Delivers SMSs asynchronously through ActiveJob
3
4
  class Cellular::Jobs::AsyncMessenger < ActiveJob::Base
4
5
  queue_as :cellular
5
6
 
@@ -1,12 +1,9 @@
1
1
  module Cellular
2
-
3
- # An API compatible logger when not in a rails environment
4
- # logs to stdout
2
+ # API compatible logger when not in a Rails environment
3
+ # Logs to stdout
5
4
  class Logger
6
5
  def info(message)
7
6
  $stdout.puts message
8
7
  end
9
8
  end
10
-
11
9
  end
12
-
@@ -1,10 +1,11 @@
1
1
  require 'active_support/time'
2
2
 
3
3
  module Cellular
4
+ # Represents an SMS
4
5
  class SMS
5
-
6
6
  attr_accessor :recipient, :sender, :message, :price, :country_code
7
7
  attr_accessor :recipients, :delivery_status, :delivery_message
8
+
8
9
  def initialize(options = {})
9
10
  @backend = Cellular.config.backend
10
11
 
@@ -24,15 +25,17 @@ module Cellular
24
25
  end
25
26
 
26
27
  def deliver_async(delivery_options = {})
27
- Cellular::Jobs::AsyncMessenger.set(delivery_options).perform_later(options)
28
+ Cellular::Jobs::AsyncMessenger.set(delivery_options)
29
+ .perform_later(options)
28
30
  end
31
+
29
32
  alias_method :deliver_later, :deliver_async
30
33
 
31
- def save(options = {})
34
+ def save(_options = {})
32
35
  raise NotImplementedError
33
36
  end
34
37
 
35
- def receive(options = {})
38
+ def receive(_options = {})
36
39
  raise NotImplementedError
37
40
  end
38
41
 
@@ -52,6 +55,5 @@ module Cellular
52
55
  country_code: @country_code
53
56
  }
54
57
  end
55
-
56
58
  end
57
59
  end
@@ -1,3 +1,3 @@
1
1
  module Cellular
2
- VERSION = '2.1.0'
2
+ VERSION = '2.2.0'.freeze
3
3
  end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cellular::Backends::Backend do
4
+ describe '::deliver' do
5
+ it 'has not been implemented yet' do
6
+ expect do
7
+ described_class.deliver
8
+ end.to raise_error NotImplementedError
9
+ end
10
+ end
11
+
12
+ describe '::receive' do
13
+ it 'has not been implemented yet' do
14
+ expect do
15
+ described_class.receive ''
16
+ end.to raise_error NotImplementedError
17
+ end
18
+ end
19
+ end
@@ -1,20 +1,19 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Cellular::Backends::CoolSMS do
4
-
5
4
  let(:recipient) { '47xxxxxxxx' }
6
5
  let(:sender) { 'Custom sender' }
7
6
  let(:message) { 'This is an SMS message' }
8
7
 
9
- let(:options) {
8
+ let(:options) do
10
9
  {
11
10
  recipient: recipient,
12
11
  sender: sender,
13
12
  message: message
14
13
  }
15
- }
14
+ end
16
15
 
17
- let(:payload) {
16
+ let(:payload) do
18
17
  {
19
18
  username: 'username',
20
19
  password: 'password',
@@ -25,7 +24,7 @@ describe Cellular::Backends::CoolSMS do
25
24
  resulttype: 'xml',
26
25
  lang: 'en'
27
26
  }
28
- }
27
+ end
29
28
 
30
29
  before do
31
30
  Cellular.config.username = 'username'
@@ -34,13 +33,15 @@ describe Cellular::Backends::CoolSMS do
34
33
 
35
34
  describe '::deliver' do
36
35
  before do
37
- stub_request(:get, 'https://sms.coolsmsc.dk/?charset=utf-8&from=Custom%20sender&lang=en&message=This%20is%20an%20SMS%20message&password=password&resulttype=xml&to=47xxxxxxxx&username=username').
38
- to_return headers: { 'Content-Type' => 'text/xml' }, body: fixture('backends/cool_sms/success.xml')
36
+ stub_request(:get, 'https://sms.coolsmsc.dk/?charset=utf-8&from=Custom%20sender&lang=en&message=This%20is%20an%20SMS%20message&password=password&resulttype=xml&to=47xxxxxxxx&username=username')
37
+ .to_return headers: { 'Content-Type' => 'text/xml' }, body: fixture('backends/cool_sms/success.xml')
39
38
  end
40
39
 
41
40
  it 'uses HTTParty to deliver an SMS' do
42
- expect(HTTParty).to receive(:get).with(described_class::GATEWAY_URL, query:
43
- payload).and_call_original
41
+ expect(HTTParty).to receive(:get).with(
42
+ described_class::GATEWAY_URL,
43
+ query: payload
44
+ ).and_call_original
44
45
 
45
46
  described_class.deliver(options)
46
47
  end
@@ -56,8 +57,8 @@ describe Cellular::Backends::CoolSMS do
56
57
 
57
58
  context 'when not successful' do
58
59
  before do
59
- stub_request(:get, 'https://sms.coolsmsc.dk/?charset=utf-8&from=Custom%20sender&lang=en&message=This%20is%20an%20SMS%20message&password=password&resulttype=xml&to=47xxxxxxxx&username=username').
60
- to_return headers: { 'Content-Type' => 'text/xml' }, body: fixture('backends/cool_sms/failure.xml')
60
+ stub_request(:get, 'https://sms.coolsmsc.dk/?charset=utf-8&from=Custom%20sender&lang=en&message=This%20is%20an%20SMS%20message&password=password&resulttype=xml&to=47xxxxxxxx&username=username')
61
+ .to_return headers: { 'Content-Type' => 'text/xml' }, body: fixture('backends/cool_sms/failure.xml')
61
62
  end
62
63
 
63
64
  it 'returns failure and a message' do
@@ -70,12 +71,14 @@ describe Cellular::Backends::CoolSMS do
70
71
  end
71
72
 
72
73
  describe '::parse_response' do
73
- it 'should return the correct response' do
74
+ it 'returns the correct response' do
74
75
  message = ['success', 'The message was sent correctly.']
75
76
 
76
77
  check = { 'status' => message[0], 'result' => message[1] }
77
- second_check = { 'status' => message[0],
78
- 'message' => { 'result' => message[1] } }
78
+ second_check = {
79
+ 'status' => message[0],
80
+ 'message' => { 'result' => message[1] }
81
+ }
79
82
 
80
83
  expect(described_class.parse_response(check)).to eq(message)
81
84
  expect(described_class.parse_response(second_check)).to eq(message)
@@ -83,31 +86,32 @@ describe Cellular::Backends::CoolSMS do
83
86
  end
84
87
 
85
88
  describe '::coolsms_config' do
86
- it 'should return the config for coolsms' do
89
+ it 'returns the config for coolsms' do
87
90
  expect(described_class.coolsms_config).to eq(
88
- {
89
- username: Cellular.config.username,
90
- password: Cellular.config.password
91
- })
91
+ username: Cellular.config.username,
92
+ password: Cellular.config.password
93
+ )
92
94
  end
93
95
  end
94
96
 
95
97
  describe '::defaults_with' do
96
- it 'should return the whole query' do
98
+ it 'returns the whole query' do
97
99
  options[:batch] = recipient
98
100
  expect(described_class.defaults_with(options)).to eq(payload)
99
101
  end
100
102
  end
101
103
 
102
104
  describe '::recipients_batch' do
103
- it 'should wrap recipient option into a array' do
104
- expect(described_class.recipients_batch({recipient: recipient}))
105
- .to eq([recipient])
105
+ it 'wraps recipient option into an array' do
106
+ result = described_class.recipients_batch(recipient: recipient)
107
+ expect(result).to eq([recipient])
106
108
  end
107
- it 'should return recipients option as it is' do
108
- expect(described_class.recipients_batch({recipients: [recipient,recipient]}))
109
- .to eq([recipient,recipient])
109
+
110
+ it 'returns recipients option as-is' do
111
+ result = described_class.recipients_batch(
112
+ recipients: [recipient, recipient]
113
+ )
114
+ expect(result).to eq([recipient, recipient])
110
115
  end
111
116
  end
112
-
113
117
  end