tms_client 0.1.0 → 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b7a0be5e64161ecf0605c965d2bb949ec4b157db
4
+ data.tar.gz: 6f725c6cb7c2f771dd7cbc07d67f00ad113af1fb
5
+ SHA512:
6
+ metadata.gz: 8ede49f2709329392caa5e954071b769f136abe14f6ad32f7f1f8ca617314fe0f07a4025c6e1dc2d6cd7bd4298bc0d8d961381d366e46671d38da898a2b16197
7
+ data.tar.gz: 4bc8ad9a68092470eea57fd069d6cc6fba3c1b0491f0c28489be27141a3ec25f8f760078e2e53ec76ced4edb35cf4062d72cd03068a323ade5f46b50f9303abb
data/Gemfile CHANGED
@@ -1,11 +1,13 @@
1
1
  source "https://rubygems.org"
2
2
  gemspec
3
3
 
4
- group :development do
4
+ group :development, :test do
5
5
  gem 'rspec'
6
6
  gem 'rake'
7
- gem 'redcarpet'
7
+ gem 'redcarpet', :platform => :ruby
8
8
  gem 'yard'
9
9
  gem 'guard-rspec'
10
- gem 'rubygems-tasks', :git => 'https://github.com/postmodern/rubygems-tasks.git'
11
- end
10
+ gem 'mail'
11
+ gem 'rubygems-tasks', :git => 'https://github.com/postmodern/rubygems-tasks.git', :platform => :ruby
12
+ end
13
+
data/README.md CHANGED
@@ -51,14 +51,15 @@ message.href # "/messages/sms/87"
51
51
  message.get # <TMS::SmsMessage href=/messages/sms/87 attributes={...}>
52
52
  ```
53
53
 
54
- ### Sending Email
54
+ ### Sending an Email
55
55
 
56
56
  ```ruby
57
- message = client.email_messages.build(:body=>'<p><a href="http://example.com">Visit here</a>', :subject => 'Hey')
57
+ message = client.email_messages.build(:body=>'<p><a href="http://example.com">Visit here</a>',
58
+ :subject => 'Hey')
58
59
  message.recipients.build(:email=>'example1@example.com')
59
- message.recipients.build(:email=>'example2@example.com')
60
+ message.recipients.build(:email=>'')
60
61
  message.post # true
61
- message.recipients.collection.detect{|r| r.errors } # {"phone"=>["is not a number"]}
62
+ message.recipients.collection.detect{|r| r.errors } # {"email"=>["can't be blank"]}
62
63
  # save succeeded, but we have one bad recipient
63
64
  message.href # "/messages/email/87"
64
65
  message.get # <TMS::EmailMessage href=/messages/email/88 attributes={...}>
@@ -113,8 +114,9 @@ Command Types are the available commands that can be used to respond to an incom
113
114
  ```ruby
114
115
  command_types = client.command_types.get
115
116
  command_types.collection.each do |at|
116
- puts at.name # "forward"
117
- puts at.fields # ["url", "http_method", ...]
117
+ puts at.name # "forward"
118
+ puts at.string_fields # ["url", ...]
119
+ puts at.array_fields # ["foo", ...]
118
120
  end
119
121
  ```
120
122
 
@@ -123,7 +125,7 @@ Keywords are chunks of text that are used to match an incoming SMS message.
123
125
 
124
126
  ```ruby
125
127
  # CRUD
126
- keyword = client.keywords.build(:name => "BUSRIDE")
128
+ keyword = client.keywords.build(:name => "BUSRIDE", :response_text => "Visit example.com/rides for more info")
127
129
  keyword.post # true
128
130
  keyword.name # 'busride'
129
131
  keyword.name = "TRAINRIDE"
@@ -134,7 +136,7 @@ keyword.delete # true
134
136
  # list
135
137
  keywords = client.keywords.get
136
138
  keywords.collection.each do |k|
137
- puts k.name
139
+ puts k.name, k.response_text
138
140
  end
139
141
  ```
140
142
 
@@ -174,6 +176,27 @@ logger = Logger.new(STDOUT)
174
176
  client = TMS::Client.new('username', 'password', :logger => logger)
175
177
  ```
176
178
 
179
+ ActionMailer integration
180
+ ------------------------
181
+
182
+ You can use TMS from the mail gem or ActionMailer as a delivery method.
183
+
184
+ Gemfile
185
+ ```ruby
186
+ gem 'tms_client', :require=>'tms_client/mail/delivery_method'
187
+ ```
188
+
189
+ config/environment.rb
190
+ ```ruby
191
+ config.action_mailer.delivery_method = :govdelivery_tms
192
+ config.action_mailer.govdelivery_tms_settings = {
193
+ :username=>'email@foo.com',
194
+ :password=>'pass',
195
+ :api_root=>'https://stage-tms.govdelivery.com'
196
+ }
197
+ ```
198
+
199
+
177
200
  Generating Documentation
178
201
  ------------------------
179
202
  This project uses [yard](https://github.com/lsegal/yard) to generate documentation. To generate API documentation yourself, use the following series of commands from the project root:
@@ -5,18 +5,22 @@ class TMS::Client
5
5
 
6
6
  attr_accessor :connection, :href
7
7
 
8
+ DEFAULTS = {:api_root => 'http://localhost:3000', :logger => nil}.freeze
9
+
8
10
  # Create a new client and issue a request for the available resources for a given account.
9
11
  #
10
- # === Options
11
- # * +:api_root+ - The root URL of the TMS api. Defaults to localhost:3000
12
- # * +:logger+ - An instance of a Logger class (http transport information will be logged here) - defaults to nil
12
+ # @param [String] username The username of your account
13
+ # @param [String] password The password of your account
14
+ # @param [Hash] options
15
+ # @option options [String] :api_root The root URL of the TMS api. Defaults to localhost:3000
16
+ # @option options [Logger] :logger An instance of a Logger class (http transport information will be logged here) - defaults to nil
13
17
  #
14
- # === Examples
18
+ # @example
15
19
  # client = TMS::Client.new("foo@example.com", "onetwothree", {
16
20
  # :api_root => "https://tms.govdelivery.com",
17
21
  # :logger => Logger.new(STDOUT)})
18
22
  #
19
- def initialize(username, password, options = {:api_root => 'http://localhost:3000', :logger => nil})
23
+ def initialize(username, password, options = DEFAULTS)
20
24
  @api_root = options[:api_root]
21
25
  connect!(username, password, options[:logger])
22
26
  discover!
@@ -1,5 +1,38 @@
1
1
  module TMS
2
2
  module Errors
3
+ class ServerError
4
+ def initialize(response)
5
+ super("TMS client encountered a server error: #{response.status} \n#{response.body}")
6
+ end
7
+ end
8
+ class NoRelation < StandardError
9
+ def initialize(rel=nil, obj=nil)
10
+ message = "no link relation "
11
+ message << "'#{rel}' " if rel
12
+ message << 'is available'
13
+ message << " for #{obj}" if obj
14
+ super(message)
15
+ end
16
+ end
17
+ class InvalidVerb
18
+ attr_reader :record
19
+
20
+ def initialize(record_or_string)
21
+ if record_or_string.respond_to?(:href)
22
+ @record = record_or_string
23
+ super("Couldn't POST #{record.class} to #{record.href}: #{record.errors.join(', ')}")
24
+ else
25
+ super(record_or_string)
26
+ end
27
+
28
+ end
29
+ end
30
+ class InvalidPost < InvalidVerb
31
+ end
32
+ class InvalidPut < InvalidVerb
33
+ end
34
+ class InvalidDelete < InvalidVerb
35
+ end
3
36
  class InvalidGet < StandardError
4
37
  def initialize(message=nil)
5
38
  super(message || "Can't GET a resource after an invalid POST; either create a new object or fix errors")
@@ -6,7 +6,7 @@ module TMS::InstanceResource
6
6
  end
7
7
 
8
8
  module ClassMethods
9
- #
9
+ ##
10
10
  # Writeable attributes are sent on POST/PUT.
11
11
  #
12
12
  def writeable_attributes(*attrs)
@@ -18,7 +18,7 @@ module TMS::InstanceResource
18
18
  @writeable_attributes
19
19
  end
20
20
 
21
- #
21
+ ##
22
22
  # Readonly attributes don't get POSTed.
23
23
  # (timestamps are included by default)
24
24
  #
@@ -31,15 +31,16 @@ module TMS::InstanceResource
31
31
  @readonly_attributes
32
32
  end
33
33
 
34
- #
34
+ ##
35
35
  # For collections that are represented as attributes (i.e. inline, no href)
36
36
  #
37
- # # message.rb
38
- # collection_attributes :recipients
37
+ # @example
38
+ # collection_attributes :recipients
39
+ #
39
40
  def collection_attributes(*attrs)
40
41
  @collection_attributes ||= []
41
42
  if attrs.any?
42
- @collection_attributes.map!(&:to_sym).concat(attrs).uniq! if attrs.any?
43
+ @collection_attributes.map!(&:to_sym).concat(attrs).uniq!
43
44
  @collection_attributes.each { |a| setup_collection(a) }
44
45
  end
45
46
  @collection_attributes
@@ -49,18 +50,32 @@ module TMS::InstanceResource
49
50
  @custom_class_names ||= {}
50
51
  end
51
52
 
52
- #
53
+ ##
53
54
  # For collections that are represented as attributes (i.e. inline, no href)
54
55
  # and that have a class name other than the one we would infer.
55
56
  #
56
- # # email.rb
57
- # collection_attributes :recipients, 'EmailRecipient'
57
+ # @example
58
+ # collection_attributes :recipients, 'EmailRecipient'
59
+ #
58
60
  def collection_attribute(attr, tms_class)
59
61
  @collection_attributes ||= []
60
62
  @collection_attributes.push(attr).uniq!
61
63
  setup_collection(attr, TMS.const_get(tms_class))
62
64
  end
63
65
 
66
+ ##
67
+ # Read-only collection attributes don't get POSTed.
68
+ # Use this for collections that are represented as attributes, but cannot be modified.
69
+ #
70
+ # @example
71
+ # readonly_collection_attribute :opens
72
+ #
73
+ def readonly_collection_attribute(attr, tms_class)
74
+ @readonly_collection_attributes ||= []
75
+ @readonly_collection_attributes.push(attr).uniq!
76
+ setup_collection(attr, TMS.const_get(tms_class))
77
+ end
78
+
64
79
  def setup_attributes(attrs, readonly=false)
65
80
  attrs.map(&:to_sym).each do |property|
66
81
  self.send :define_method, :"#{property}=", &lambda { |v| @attributes[property] = v } unless readonly
@@ -89,60 +104,24 @@ module TMS::InstanceResource
89
104
 
90
105
  def get
91
106
  raise TMS::Errors::InvalidGet if self.new_record?
92
- set_attributes_from_hash(self.client.get(href).body)
93
- self
107
+ process_response(client.get(self.href), :get)
94
108
  end
95
109
 
96
110
  def post
97
- response = client.post(self)
111
+ self.errors = nil
112
+ process_response(client.post(self), :post)
113
+ end
98
114
 
99
- case response.status
100
- when 200..299
101
- set_attributes_from_hash(response.body)
102
- self.new_record=false
103
- return true
104
- when 401
105
- raise Exception.new("401 Not Authorized")
106
- when 404
107
- raise(Exception.new("Can't POST to #{self.href}"))
108
- else
109
- if response.body['errors']
110
- self.errors = response.body['errors']
111
- end
112
- end
113
- return false
115
+ def post!
116
+ self.post or raise TMS::Errors::InvalidPost.new(self)
114
117
  end
115
118
 
116
119
  def put
117
- response = client.put(self)
118
- case response.status
119
- when 200
120
- self.new_record=false
121
- set_attributes_from_hash(response.body)
122
- return true
123
- when 401
124
- raise Exception.new("401 Not Authorized")
125
- when 404
126
- raise(Exception.new("Can't POST to #{self.href}"))
127
- else
128
- if response.body['errors']
129
- self.errors = response.body['errors']
130
- end
131
- end
132
- return false
120
+ process_response(client.put(self), :put)
133
121
  end
134
122
 
135
123
  def delete
136
- response = self.client.delete(href)
137
- case response.status
138
- when 200
139
- return true
140
- else
141
- if response.body['errors']
142
- self.errors = response.body['errors']
143
- end
144
- end
145
- return false
124
+ process_response(client.delete(self.href), :delete)
146
125
  end
147
126
 
148
127
  def to_s
@@ -166,7 +145,28 @@ module TMS::InstanceResource
166
145
  self.class.custom_class_names[rel.to_sym] || super
167
146
  end
168
147
 
169
- private
148
+ def process_response(response, method)
149
+ error_class = TMS::Errors.const_get("Invalid#{method.to_s.capitalize}")
150
+ case response.status
151
+ when 204
152
+ return true
153
+ when 200..299
154
+ set_attributes_from_hash(response.body) if response.body.is_a?(Hash)
155
+ self.new_record=false
156
+ return true
157
+ when 401
158
+ raise error_class.new("401 Not Authorized")
159
+ when 404
160
+ raise(error_class.new("Can't POST to #{self.href}"))
161
+ when 500..599
162
+ raise(TMS::Errors::ServerError.new(response))
163
+ else # 422?
164
+ if response.body['errors']
165
+ self.errors = response.body['errors']
166
+ end
167
+ end
168
+ return false
169
+ end
170
170
 
171
171
  def set_attributes_from_hash(hash)
172
172
  hash.reject { |k, _| k=~/^_/ }.each do |property, value|
@@ -0,0 +1,57 @@
1
+ require 'tms_client'
2
+ require 'mail'
3
+ require 'mail/check_delivery_params'
4
+ module TMS
5
+ module Mail
6
+ # Use TMS from the mail gem or ActionMailer as a delivery method.
7
+ #
8
+ # # Gemfile
9
+ # gem 'tms_client', :require=>'tms_client/mail/delivery_method'
10
+ #
11
+ # # config/environment.rb
12
+ # config.action_mailer.delivery_method = :govdelivery_tms
13
+ # config.action_mailer.govdelivery_tms_settings = {
14
+ # :username=>'email@foo.com',
15
+ # :password=>'pass',
16
+ # :api_root=>'https://stage-tms.govdelivery.com'
17
+ # }
18
+ class DeliveryMethod
19
+ include ::Mail::CheckDeliveryParams
20
+
21
+ def initialize(values)
22
+ self.settings = values
23
+ end
24
+
25
+ attr_accessor :settings
26
+
27
+ def deliver!(mail)
28
+ #check_params(mail)
29
+ raise TMS::Errors::NoRelation.new('email_messages', client) unless client.respond_to?(:email_messages)
30
+
31
+ envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
32
+
33
+ tms_message = client.email_messages.build(
34
+ :from_name => mail[:from].display_names.first,
35
+ :subject => mail.subject,
36
+ :body => (mail.body || mail.html_part.body || mail.text_part.body).to_s
37
+ )
38
+
39
+ mail.to.each { |recip| tms_message.recipients.build(:email => recip) }
40
+ tms_message.post!
41
+ tms_message
42
+ end
43
+
44
+ def client
45
+ @client ||= TMS::Client.new(settings[:username], settings[:password], settings)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ if defined?(ActionMailer)
52
+ ActionMailer::Base.add_delivery_method :govdelivery_tms, TMS::Mail::DeliveryMethod, {
53
+ :username => nil,
54
+ :password => nil,
55
+ :logger => ActionMailer::Base.logger,
56
+ :api_root => TMS::Client::DEFAULTS[:api_root]}
57
+ end
@@ -32,7 +32,7 @@ end
32
32
 
33
33
  # A collection of Keyword objects.
34
34
  #
35
- # === Example
35
+ # @example
36
36
  # keywords = client.keywords.get
37
37
  #
38
38
  class TMS::Keywords
@@ -49,7 +49,7 @@ end
49
49
  #
50
50
  # This resource is read-only.
51
51
  #
52
- # === Example
52
+ # @example
53
53
  # client.command_types.get
54
54
  # client.command_types.collection.each {|at| ... }
55
55
  class TMS::CommandTypes
@@ -59,3 +59,7 @@ end
59
59
  class TMS::Commands
60
60
  include TMS::CollectionResource
61
61
  end
62
+
63
+ class TMS::CommandActions
64
+ include TMS::CollectionResource
65
+ end
@@ -2,13 +2,11 @@ module TMS #:nodoc:
2
2
  # A command is a combination of behavior and parameters that should be executed
3
3
  # when an incoming SMS message matches the associated Keyword.
4
4
  #
5
- # ==== Attributes
5
+ # @attr name [String] The name of the command. This will default to the command_type if not supplied.
6
+ # @attr command_type [String] The type of this command. A list of valid types can be found by querying the CommandType list.
7
+ # @attr params [Hash] A Hash of string/string pairs used as configuration for this command.
6
8
  #
7
- # * +name+ - The name of the command. This will default to the command_type if not supplied.
8
- # * +command_type+ - The type of this command. A list of valid types can be found by querying the CommandType list.
9
- # * +params+ - A Hash of string/string pairs used as configuration for this command.
10
- #
11
- # === Examples
9
+ # @example
12
10
  # command = keyword.commands.build(:name => "subscribe to news", :command_type => "dcm_subscribe", :dcm_account_code => "NEWS", :dcm_topic_codes => "NEWS_1, NEWS_2")
13
11
  # command.post
14
12
  # command.dcm_topic_codes += ", NEWS_5"
@@ -0,0 +1,17 @@
1
+ module TMS #:nodoc:
2
+ # CommandAction object represent the results of Commands for a particular input (e.g. an incoming SMS message)
3
+ #
4
+ # This resource is read-only.
5
+ #
6
+ # ==== Attributes
7
+ #
8
+ # * +http_response_code+ - e.g. 200, 404, etc.
9
+ # * +http_content_type+ - text/html, etc.
10
+ # * +http_body+ - Request body (if it's <500 characters, otherwise it'll be nil)
11
+ #
12
+ class CommandAction
13
+ include InstanceResource
14
+
15
+ readonly_attributes :status, :content_type, :response_body, :created_at
16
+ end
17
+ end
@@ -1,20 +1,20 @@
1
1
  module TMS #:nodoc:
2
- # CommandType is a pair of values (name, fields) that can be attached
2
+ # CommandType is a pair of values (name, string_fields, array_fields) that can be attached
3
3
  # to a Keyword (in a Command object).
4
4
  #
5
5
  # This resource is read-only.
6
6
  #
7
- # ==== Attributes
8
- #
9
- # * +name+ - The name of the CommandType.
10
- # * +fields+ - An Array of strings representing the different fields on this
7
+ # @attr name [String] The name of the CommandType.
8
+ # @attr string_fields [Array] An Array of strings representing the different string_fields on this
11
9
  # CommandType. Field values will always be strings.
10
+ # @attr array_fields [Array] An array of strings representing the different array fields on this
11
+ # CommandType. Field values will always be arrays of strings.
12
12
  #
13
13
  class CommandType
14
14
 
15
15
  include InstanceResource
16
16
 
17
- # @!parse attr_reader :fields, :name
18
- readonly_attributes :name, :fields
17
+ # @!parse attr_reader :string_fields, :array_fields, :name
18
+ readonly_attributes :name, :string_fields, :array_fields
19
19
  end
20
20
  end
@@ -2,37 +2,44 @@ module TMS #:nodoc:
2
2
  # An EmailMessage is used to create and send a email to a collection of EmailRecipient
3
3
  # objects. Certain metrics are available after the email is sent, including
4
4
  # the collection of recipients who clicked or opened the email.
5
- #
6
- #
7
- # ==== Attributes
8
- #
9
- # * +from_name+ - The name of the person or entity sending the email.
10
- # * +subject+ - The subject of the email
11
- # * +body+ - The body of the email
12
5
  #
6
+ # @attr from_name [String] The name of the person or entity sending the email.
7
+ # @attr subject [String] The subject of the email
8
+ # @attr body [String] The body of the email
9
+ # @attr open_tracking_enabled [Boolean] Whether to track opens on this message. Optional, defaults to true.
10
+ # @attr click_tracking_enabled [Boolean] Whether to track clicks on links in this message. Optional, defaults to true.
11
+ # @attr macros [Hash] A dictionary of key/value pairs to use in the subject and body as default macros.
12
+ # The message-level macros are used when a recipient has no value for a given macro key.
13
13
  #
14
- # === Example
15
- # Sending a message
14
+ # @example Sending a message
16
15
  # email_message = client.email_messages.build(:subject => "Great news!", :body => "You win! <a href='http://example.com/'>click here</a>.")
17
16
  # email_message.recipients.build(:email => "john@example.com")
18
17
  # email_message.recipients.build(:email => "jeff@example.com")
19
18
  # email_message.post
20
19
  # email_message.get
21
20
  #
22
- # Viewing recipients that clicked on a link in the email
21
+ # @example Viewing recipients that clicked on a link in the email
23
22
  # email_message.get
24
23
  # email_message.clicked.get
25
24
  # email_message.clicked.collection # => [<#EmailRecipient>,...]
26
25
  #
27
- # Viewing recipients that opened the email
26
+ # @example Viewing recipients that opened the email
28
27
  # email_message.get
29
28
  # email_message.opened.get
30
29
  # email_message.opened.collection # => [<#EmailRecipient>,...]
30
+ #
31
+ # @example Using macros
32
+ # email_message = client.email_messages.build(:subject => "Hello [[user]]",
33
+ # :body => "Your name is [[name]]",
34
+ # :macros => {:user => "Sir or Madam", :name => "unknown"})
35
+ # email_message.recipients.build(:email => "jeff@example.com", :macros => {:user => "jexample", :name => "Jeff Example"})
36
+ # email_message.post
37
+ #
31
38
  class EmailMessage
32
39
  include InstanceResource
33
40
 
34
- # @!parse attr_accessor :body, :from_name, :subject
35
- writeable_attributes :body, :from_name, :subject
41
+ # @!parse attr_accessor :body, :from_name, :subject, :open_tracking_enabled, :click_tracking_enabled, :macros
42
+ writeable_attributes :body, :from_name, :subject, :open_tracking_enabled, :click_tracking_enabled, :macros
36
43
 
37
44
  # @!parse attr_reader :created_at, :status
38
45
  readonly_attributes :created_at, :status
@@ -1,19 +1,31 @@
1
1
  module TMS #:nodoc:
2
+ # An EmailRecipient is used in conjunction with an EmailMessage to send email.
3
+ #
4
+ # @attr email [String] The recipient email address
5
+ # @attr macros [Hash] A dictionary of key/value pairs to resolve in the subject and body as macros. This value can be nil.
6
+ #
7
+ # @example Sending a message
8
+ # email_message = client.email_messages.build(:subject => "Great news!", :body => "You win! <a href='http://example.com/'>click here</a>.")
9
+ # email_message.recipients.build(:email => "john@example.com")
10
+ # email_message.recipients.build(:email => "jeff@example.com")
11
+ # email_message.post
12
+ # email_message.get
13
+ #
2
14
  class EmailRecipient
3
15
  include InstanceResource
4
16
 
5
- # @!parse attr_accessor :email
6
- writeable_attributes :email
17
+ # @!parse attr_accessor :email, :macros
18
+ writeable_attributes :email, :macros
7
19
 
8
20
  # @!parse attr_reader :completed_at
9
21
  readonly_attributes :completed_at
10
22
 
11
23
  ##
12
24
  # A CollectionResource of EmailRecipientOpens for this EmailRecipient
13
- collection_attribute :opens, 'EmailRecipientOpens'
25
+ readonly_collection_attribute :opens, 'EmailRecipientOpens'
14
26
 
15
27
  ##
16
28
  # A CollectionResource of EmailRecipientClicks for this EmailRecipient
17
- collection_attribute :clicks, 'EmailRecipientClicks'
29
+ readonly_collection_attribute :clicks, 'EmailRecipientClicks'
18
30
  end
19
31
  end
@@ -2,7 +2,7 @@ module TMS #:nodoc:
2
2
  class InboundSmsMessage
3
3
  include InstanceResource
4
4
 
5
- # @!parse attr_reader :created_at, :completed_at, :from, :body, :to
6
- readonly_attributes :created_at, :completed_at, :from, :body, :to
5
+ # @!parse attr_reader :created_at, :completed_at, :from, :body, :to, :command_status
6
+ readonly_attributes :created_at, :completed_at, :from, :body, :to, :command_status, :keyword_response
7
7
  end
8
8
  end
@@ -1,22 +1,26 @@
1
1
  module TMS #:nodoc:
2
2
  # A Keyword is a word that TMS will detect in an incoming SMS message. Keywords can have Commands, and
3
- # when an incoming text message has a keyword, TMS will execute the keyword's Commands.
3
+ # when an incoming text message has a keyword, TMS will execute the keyword's Commands. Keywords may
4
+ # also have a response text field. If the response text is not blank, the system will send an SMS reply to the user
5
+ # immediately with the given text.
4
6
  #
5
- # ==== Attributes
6
- #
7
- # * +name+ - The name of the keyword.
7
+ # @attr name [String] The name of the keyword. The system will scan an incoming SMS for this string (in a case-insensitive manner).
8
+ # @attr response_text [String] (Optional) The static text with which to reply to an SMS to this keyword.
9
+ # This value can be blank, in which case the handset user will not receive a response.
10
+ # Note that all keyword commands will be executed, regardless of the value of response_text.
8
11
  #
9
- # === Examples
12
+ # @example
10
13
  # keyword = client.keywords.build(:name => "HOWDY")
11
14
  # keyword.post
12
- # keyword.name = "DOODY"
15
+ # keyword.name = "INFO"
16
+ # keyword.response_text = "Please call our support staff at 1-555-555-5555"
13
17
  # keyword.put
14
18
  # keyword.delete
15
19
  class Keyword
16
20
  include InstanceResource
17
21
 
18
- # @!parse attr_accessor :name
19
- writeable_attributes :name
22
+ # @!parse attr_accessor :name, :response_text
23
+ writeable_attributes :name, :response_text
20
24
 
21
25
  ##
22
26
  # A CollectionResource of Command objects
@@ -3,11 +3,9 @@ module TMS #:nodoc:
3
3
  # objects.
4
4
  #
5
5
  #
6
- # ==== Attributes
6
+ # @attr body [String] The content of the SMS. This field will be truncated to 160 characters.
7
7
  #
8
- # * +body+ - The content of the SMS. This field will be truncated to 160 characters.
9
- #
10
- # === Example
8
+ # @example
11
9
  # sms = client.sms_messages.build(:body => "Hello")
12
10
  # sms.recipients.build(:phone => "+18001002000")
13
11
  # sms.post
@@ -4,11 +4,9 @@ module TMS #:nodoc:
4
4
  # played to them. Accepted sound formats include +wav+, +mp3+, and +aiff+.
5
5
  #
6
6
  #
7
- # ==== Attributes
7
+ # @attr play_url [String] The url to the sound file to be played back to the call recipients
8
8
  #
9
- # * +play_url+ - The url to the sound file to be played back to the call recipients
10
- #
11
- # === Example
9
+ # @example
12
10
  # voice_message = client.voice_messages.build(:play_url => "http://example.com/emergency_weather.mp3")
13
11
  # voice_message.recipients.build(:phone => "+18001002000")
14
12
  # voice_message.post
@@ -1,3 +1,3 @@
1
1
  module TMS #:nodoc:
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/lib/tms_client.rb CHANGED
@@ -28,6 +28,7 @@ require 'tms_client/resource/voice_message'
28
28
  require 'tms_client/resource/email_message'
29
29
  require 'tms_client/resource/inbound_sms_message'
30
30
  require 'tms_client/resource/command_type'
31
+ require 'tms_client/resource/command_action'
31
32
  require 'tms_client/resource/command'
32
33
  require 'tms_client/resource/keyword'
33
34
 
@@ -9,12 +9,21 @@ describe TMS::CommandTypes do
9
9
  @command_types = TMS::CommandTypes.new(client, '/command_types')
10
10
  end
11
11
  it 'should GET ok' do
12
- body = [{"fields"=>["dcm_account_codes"], "name"=>"dcm_unsubscribe"},
13
- {"fields"=>["dcm_account_code", "dcm_topic_codes"], "name"=>"dcm_subscribe"},
14
- {"fields"=>["http_method", "username", "password", "url"], "name"=>"forward"}]
12
+ body = [{"name"=>"dcm_unsubscribe",
13
+ "string_fields"=>[],
14
+ "array_fields"=>["dcm_account_codes"]},
15
+ {"name"=>"dcm_subscribe",
16
+ "string_fields"=>["dcm_account_code"],
17
+ "array_fields"=>["dcm_topic_codes"]},
18
+ {"name"=>"forward",
19
+ "string_fields"=>["http_method", "username", "password", "url"],
20
+ "array_fields"=>[]}]
15
21
  @command_types.client.should_receive(:get).and_return(double('response', :body => body, :status => 200, :headers => {}))
16
22
  @command_types.get
17
23
  @command_types.collection.length.should == 3
24
+ ct = @command_types.collection.find{|c| c.name == "dcm_subscribe"}
25
+ ct.array_fields.should eq(["dcm_topic_codes"])
26
+ ct.string_fields.should eq(["dcm_account_code"])
18
27
  end
19
28
  end
20
29
  end
@@ -3,6 +3,7 @@ class Foo
3
3
  include TMS::InstanceResource
4
4
  writeable_attributes :bar
5
5
  collection_attribute :blah, 'EmailMessage'
6
+ readonly_collection_attribute :shah, 'EmailMessage'
6
7
  end
7
8
 
8
9
  describe TMS::InstanceResource do
@@ -27,6 +28,7 @@ describe TMS::InstanceResource do
27
28
 
28
29
  it 'should correctly reflect on collection resources' do
29
30
  @instance_resource.blah.class.should == TMS::EmailMessage
31
+ @instance_resource.shah.class.should == TMS::EmailMessage
30
32
  end
31
33
  end
32
34
  end
data/spec/keyword_spec.rb CHANGED
@@ -6,22 +6,25 @@ describe TMS::Keyword do
6
6
  double('client')
7
7
  end
8
8
  before do
9
- @keyword = TMS::Keyword.new(client, nil, {:name => 'LOL'})
9
+ @keyword = TMS::Keyword.new(client, nil, {:name => 'LOL', :response_text => 'very funny!'})
10
10
  end
11
11
  it 'should initialize with attrs' do
12
12
  @keyword.name.should == 'LOL'
13
+ @keyword.response_text.should == 'very funny!'
13
14
  end
14
15
  it 'should post successfully' do
15
16
  response = {:name => 'lol'}
16
17
  @keyword.client.should_receive('post').with(@keyword).and_return(double('response', :status => 201, :body => response))
17
18
  @keyword.post
18
19
  @keyword.name.should == 'lol'
20
+ @keyword.response_text.should == 'very funny!'
19
21
  end
20
22
  it 'should handle errors' do
21
23
  response = {'errors' => {:name => "can't be nil"}}
22
24
  @keyword.client.should_receive('post').with(@keyword).and_return(double('response', :status => 422, :body => response))
23
25
  @keyword.post
24
26
  @keyword.name.should == 'LOL'
27
+ @keyword.response_text.should == 'very funny!'
25
28
  @keyword.errors.should == {:name => "can't be nil"}
26
29
  end
27
30
  end
@@ -35,18 +38,19 @@ describe TMS::Keyword do
35
38
  @keyword = TMS::Keyword.new(client, '/keywords/99', {})
36
39
  end
37
40
  it 'should GET cleanly' do
38
- response = {:name => 'FOO'}
41
+ response = {:name => 'FOO', :response_text => 'hello'}
39
42
  @keyword.client.should_receive('get').with(@keyword.href).and_return(double('response', :status => 200, :body => response))
40
43
  @keyword.get
41
44
  @keyword.name.should == 'FOO'
45
+ @keyword.response_text.should == 'hello'
42
46
  end
43
47
  it 'should PUT cleanly' do
44
48
  @keyword.name = "GOVLIE"
45
- response = {:name => 'govlie'}
49
+ response = {:name => 'govlie', :response_text => nil}
46
50
  @keyword.client.should_receive('put').with(@keyword).and_return(double('response', :status => 200, :body => response))
47
51
  @keyword.put
48
52
  @keyword.name.should == 'govlie'
49
-
53
+ @keyword.response_text.should be_nil
50
54
  end
51
55
  it 'should DELETE cleanly' do
52
56
  @keyword.client.should_receive('delete').with(@keyword.href).and_return(double('response', :status => 200, :body => ''))
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'mail'
3
+ require 'tms_client/mail/delivery_method'
4
+ describe TMS::Mail::DeliveryMethod do
5
+ subject { TMS::Mail::DeliveryMethod.new({}) }
6
+ let(:client) { stub('TMS::Client') }
7
+ let(:email_messages) { double('email_messages') }
8
+ let(:tms_message) { double('tms_message', :recipients => double(:build => TMS::Recipient.new('href'))) }
9
+
10
+ it 'should work with a basic Mail::Message' do
11
+ mail = Mail.new do
12
+ subject 'hi'
13
+ from '"My mom" <my@mom.com>'
14
+ to '"A Nice Fellow" <tyler@sink.govdelivery.com>'
15
+ body '<blink>HI</blink>'
16
+ end
17
+ client.stub(:email_messages).and_return(email_messages)
18
+ subject.stub(:client).and_return(client)
19
+ email_messages.should_receive(:build).with(
20
+ :from_name => mail[:from].display_names.first,
21
+ :subject => mail.subject,
22
+ :body => mail.body.to_s || mail.html_part.body.to_s || mail.text_part.body.to_s
23
+ ).and_return(tms_message)
24
+ tms_message.should_receive(:post!).and_return(true)
25
+
26
+ subject.deliver!(mail)
27
+ end
28
+
29
+ end
metadata CHANGED
@@ -1,65 +1,58 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tms_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
5
- prerelease:
4
+ version: 0.1.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - GovDelivery
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-02-27 00:00:00.000000000 Z
11
+ date: 2013-06-01 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: faraday
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
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
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: faraday_middleware
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :runtime
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
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: activesupport
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
- description: ! 'A reference implementation, written in Ruby, to interact with GovDelivery''s
55
+ description: 'A reference implementation, written in Ruby, to interact with GovDelivery''s
63
56
  TMS API. The client is compatible with Ruby versions 1.8.7 and 1.9.3. '
64
57
  email:
65
58
  - support@govdelivery.com
@@ -79,9 +72,11 @@ files:
79
72
  - lib/tms_client/instance_resource.rb
80
73
  - lib/tms_client/link_header.rb
81
74
  - lib/tms_client/logger.rb
75
+ - lib/tms_client/mail/delivery_method.rb
82
76
  - lib/tms_client/request.rb
83
77
  - lib/tms_client/resource/collections.rb
84
78
  - lib/tms_client/resource/command.rb
79
+ - lib/tms_client/resource/command_action.rb
85
80
  - lib/tms_client/resource/command_type.rb
86
81
  - lib/tms_client/resource/email_message.rb
87
82
  - lib/tms_client/resource/email_recipient.rb
@@ -102,33 +97,33 @@ files:
102
97
  - spec/instance_resource_spec.rb
103
98
  - spec/keyword_spec.rb
104
99
  - spec/keywords_spec.rb
100
+ - spec/mail/delivery_method_spec.rb
105
101
  - spec/message_spec.rb
106
102
  - spec/messages_spec.rb
107
103
  - spec/spec_helper.rb
108
104
  - spec/tms_client_spec.rb
109
105
  homepage: http://govdelivery.com
110
106
  licenses: []
107
+ metadata: {}
111
108
  post_install_message:
112
109
  rdoc_options: []
113
110
  require_paths:
114
111
  - lib
115
112
  required_ruby_version: !ruby/object:Gem::Requirement
116
- none: false
117
113
  requirements:
118
- - - ! '>='
114
+ - - '>='
119
115
  - !ruby/object:Gem::Version
120
116
  version: '0'
121
117
  required_rubygems_version: !ruby/object:Gem::Requirement
122
- none: false
123
118
  requirements:
124
- - - ! '>='
119
+ - - '>='
125
120
  - !ruby/object:Gem::Version
126
121
  version: '0'
127
122
  requirements: []
128
123
  rubyforge_project:
129
- rubygems_version: 1.8.25
124
+ rubygems_version: 2.0.3
130
125
  signing_key:
131
- specification_version: 3
126
+ specification_version: 4
132
127
  summary: A ruby client to interact with the GovDelivery TMS REST API.
133
128
  test_files:
134
129
  - spec/client_spec.rb
@@ -137,6 +132,7 @@ test_files:
137
132
  - spec/instance_resource_spec.rb
138
133
  - spec/keyword_spec.rb
139
134
  - spec/keywords_spec.rb
135
+ - spec/mail/delivery_method_spec.rb
140
136
  - spec/message_spec.rb
141
137
  - spec/messages_spec.rb
142
138
  - spec/spec_helper.rb