action_texter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []