cellular 2.1.0 → 2.2.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.
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