action_texter 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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .idea
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2012, 2013, Watu
3
+
4
+ source "https://rubygems.org"
5
+
6
+ # Specify your gem's dependencies in action_texter.gemspec
7
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Watu
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # ActionTexter
2
+
3
+ Generic interface to send SMSs with Ruby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'action_texter'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install action_texter
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,19 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2013, Watu
3
+
4
+ require File.expand_path('../lib/action_texter/version', __FILE__)
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.authors = ["J. Pablo Fernández"]
8
+ gem.email = ["info@watuhq.com"]
9
+ gem.description = %q{Generic interface to send SMS with Ruby}
10
+ gem.summary = %q{Generic interface to send SMS with Ruby}
11
+ gem.homepage = ""
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.name = "action_texter"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = ActionTexter::VERSION
19
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2012, 2013, Watu
3
+
4
+ require "action_texter/version"
5
+ require "action_texter/client"
6
+ require "action_texter/message"
7
+ require "action_texter/response"
8
+ require "action_texter/response"
9
+ require "action_texter/test"
10
+ require "action_texter/nexmo"
11
+
@@ -0,0 +1,37 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2012, 2013, Watu
3
+
4
+ # Parent class for all SMS clients.
5
+ #
6
+ # @abstract
7
+ class ActionTexter::Client
8
+ def self.default
9
+ @default
10
+ end
11
+
12
+ def self.default=(client)
13
+ @default = client
14
+ end
15
+
16
+ def self.setup(provider, *attrs)
17
+ provider_client =
18
+ begin
19
+ ActionTexter.const_get(provider + "Client")
20
+ rescue NameError
21
+ raise "Provider #{provider} doesn't exist."
22
+ end
23
+ self.default = provider_client.new(*attrs)
24
+ end
25
+
26
+ # Deliver a message
27
+ # @param [Message] message message to be delivered
28
+ # @returns [Response] a response
29
+ def deliver(message)
30
+ raise NotImplementedError.new("should be implemented by subclasses")
31
+ end
32
+
33
+ # @private
34
+ def to_s
35
+ "#<#{self.class.name}:#{object_id}>"
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2012, 2013, Watu
3
+
4
+ # Representation of a message
5
+ #
6
+ # TODO: Implement these fields that Nexmo can use: :status_report_req, :network_code, :vcard, :vcal, :ttl
7
+ #
8
+ # @!attribute from
9
+ # @return [String] name or phone number of the author of the message.
10
+ # @!attribute to
11
+ # @return [String] phone number of the author of the message.
12
+ # @!attribute text
13
+ # @return [String] actual message to send.
14
+ # @!attribute reference
15
+ # @return [String] a reference that can be used later on to track responses. Implemented for: Nexmo.
16
+ class ActionTexter::Message
17
+ attr_accessor :from, :to, :text, :reference
18
+
19
+ def initialize(message = {})
20
+ self.from = message[:from] || raise("A message must contain from")
21
+ self.to = message[:to] || raise("A message must contain to")
22
+ self.text = message[:text] || raise("A message must contain text")
23
+ self.reference = message[:reference]
24
+ end
25
+
26
+ def deliver(client = nil)
27
+ client ||= ActionTexter::Client.default
28
+ if client.nil?
29
+ raise "To deliver a message you need to specify a client by parameter to deliver or by ActionTexter::Client.dafault="
30
+ end
31
+ client.deliver(self)
32
+ end
33
+
34
+ # @private
35
+ def to_s
36
+ "#<#{self.class.name}:#{from}:#{to}:#{text}>"
37
+ end
38
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2012, 2013, Watu
3
+
4
+ require "bigdecimal"
5
+ require "net/http"
6
+ require "net/https"
7
+ require "json"
8
+ require "uri"
9
+
10
+ # Nexmo response
11
+ class ActionTexter::NexmoResponse < ActionTexter::Response
12
+ SUCCESS_RESPONSE_CODE = "0"
13
+
14
+ # TODO: Some of these should be moved to Response if they are common enough.
15
+ attr_reader :original, :parts_count, :parts, :cost, :remaining_balance, :reference
16
+
17
+ private
18
+
19
+ def process_response(raw)
20
+ @success = true
21
+ @original = JSON.parse(raw)
22
+ @parts_count = @original["message-count"].to_i
23
+ @cost = BigDecimal.new("0")
24
+ @reference = @original["messages"].first["client-ref"] # They should all be the same, we only record it the first time.
25
+ @remaining_balance = @original["messages"].last["remaining-balance"] # I hope the last one is the lowest one, the cost of a single message shouldn't make that big of a difference anyway.
26
+ @parts = []
27
+ error_messages = []
28
+ @original["messages"].each do |raw_part|
29
+ if @success # Update the contents of success to status of this part unless @succes is already failed.
30
+ @success = raw_part["status"] == SUCCESS_RESPONSE_CODE
31
+ end
32
+ part = {:id => raw_part["message-id"],
33
+ :to => raw_part["to"],
34
+ :success => raw_part["status"] == SUCCESS_RESPONSE_CODE}
35
+ part[:reference] = raw_part["client-ref"] if raw_part.has_key? "client-ref"
36
+ if raw_part.has_key? "message-price"
37
+ part[:cost] = raw_part["message-price"]
38
+ @cost += BigDecimal.new(raw_part["message-price"])
39
+ end
40
+ if raw_part.has_key? "remaining-balance"
41
+ part[:remaining_balance] = BigDecimal.new(raw_part["remaining-balance"])
42
+ end
43
+ if raw_part.has_key? "error-text"
44
+ part[:error_message] = raw_part["error-text"]
45
+ error_messages << part[:error_message]
46
+ end
47
+ @parts << part
48
+ end
49
+ if error_messages.any?
50
+ @error_message = error_messages.uniq.join(", ")
51
+ end
52
+ end
53
+ end
54
+
55
+ # Implementation of client for Nexmo: http://nexmo.com
56
+ class ActionTexter::NexmoClient < ActionTexter::Client
57
+ attr_accessor :key, :secret
58
+
59
+ # Create a new Nexmo client with key and secret.
60
+ #
61
+ # @param [String] key key as specified by Nexmo for authenticating.
62
+ # @param [String] secret secret as specified by Nexmo for authenticating.
63
+ def initialize(key, secret)
64
+ super()
65
+ self.key = key
66
+ self.secret = secret
67
+ end
68
+
69
+ def deliver(message)
70
+ client = Net::HTTP.new("rest.nexmo.com", 443)
71
+ client.use_ssl = true
72
+ response = client.post(
73
+ "/sms/json",
74
+ URI.encode_www_form("username" => @key,
75
+ "password" => @secret,
76
+ "from" => message.from,
77
+ "to" => message.to,
78
+ "text" => message.text,
79
+ "client-ref" => message.reference),
80
+ {"Content-Type" => "application/x-www-form-urlencoded"})
81
+
82
+ return ActionTexter::NexmoResponse.new(response.body)
83
+ end
84
+
85
+ # @private
86
+ def to_s
87
+ "#<#{self.class.name}:#{key}>"
88
+ end
89
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2012, 2013, Watu
3
+
4
+ # A response as sent from the provider.
5
+ #
6
+ # @abstract
7
+ # @!attribute [r] raw
8
+ # @return [String] the raw response as returned by the provider
9
+ # @!attribute success
10
+ # @return [Boolean] weather sending the message succeeded or not. See #success? and #failed?
11
+ # @!attribute error_message
12
+ # @return [String] a descriptive message of the error when an error happened.
13
+ class ActionTexter::Response
14
+ attr_reader :raw, :success, :error_message
15
+
16
+ def initialize(raw)
17
+ @raw = raw
18
+ process_response(raw)
19
+ end
20
+
21
+ # @return [Boolean] true when sending the message succeeded, false otherwise.
22
+ def success?
23
+ !!@success
24
+ end
25
+
26
+ # @return [Boolean] false when sending the message failed, true otherwise.
27
+ def failed?
28
+ !@success
29
+ end
30
+
31
+ # @private
32
+ def to_s
33
+ "#<#{self.class.name}:#{object_id}:#{@success ? "success" : "fail"}>"
34
+ end
35
+
36
+ private
37
+
38
+ def process_response(raw)
39
+ raise NotImplementedError.new("should be implemented by subclasses")
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2012, 2013, Watu
3
+
4
+ require "bigdecimal"
5
+
6
+ # Responses sent by #TestClient
7
+ class ActionTexter::TestResponse < ActionTexter::Response
8
+ attr_reader :parts_count, :parts, :cost, :remaining_balance, :reference, :error
9
+
10
+ private
11
+
12
+ def process_response(raw)
13
+ cost_per_message = BigDecimal("0.058")
14
+ @success = true
15
+ @original = raw
16
+ @reference = @original.reference if !@original.reference.nil?
17
+ @remaining_balance = BigDecimal.new("15.10")
18
+ @cost = BigDecimal.new("0")
19
+ @parts = []
20
+ (@original.text.length.to_f / 140).ceil.times do
21
+ @remaining_balance = @remaining_balance - cost_per_message
22
+ part = {:id => "test-response-#{Time.now.to_i}",
23
+ :to => @original.to,
24
+ :remaining_balance => @remaining_balance,
25
+ :cost => cost_per_message,
26
+ :success => true}
27
+ part[:reference] = @original.reference if !@original.reference.nil?
28
+ @cost += part[:cost]
29
+ @parts << part
30
+ end
31
+ @parts_count = @parts.count
32
+ end
33
+ end
34
+
35
+ # A client that doesn't send any message but instead stores them on an array.
36
+ class ActionTexter::TestClient < ActionTexter::Client
37
+ def initialize
38
+ @@deliveries = []
39
+ end
40
+
41
+ def deliver(message)
42
+ @@deliveries << message
43
+ ActionTexter::TestResponse.new(message)
44
+ end
45
+
46
+ # All the delivered messages so far.
47
+ #
48
+ # @return [Array<Message>] delivered messages.
49
+ def deliveries
50
+ @@deliveries
51
+ end
52
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
2
+ # Copyright © 2012, 2013, Watu
3
+
4
+ module ActionTexter
5
+ VERSION = "0.1.0"
6
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: action_texter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - J. Pablo Fernández
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-05 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Generic interface to send SMS with Ruby
15
+ email:
16
+ - info@watuhq.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE
24
+ - README.md
25
+ - Rakefile
26
+ - action_texter.gemspec
27
+ - lib/action_texter.rb
28
+ - lib/action_texter/client.rb
29
+ - lib/action_texter/message.rb
30
+ - lib/action_texter/nexmo.rb
31
+ - lib/action_texter/response.rb
32
+ - lib/action_texter/test.rb
33
+ - lib/action_texter/version.rb
34
+ homepage: ''
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.24
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Generic interface to send SMS with Ruby
58
+ test_files: []