tms_client 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +10 -1
- data/README.md +105 -51
- data/Rakefile +5 -0
- data/lib/tms_client/base.rb +10 -1
- data/lib/tms_client/client.rb +15 -15
- data/lib/tms_client/collection_resource.rb +2 -1
- data/lib/tms_client/errors.rb +9 -0
- data/lib/tms_client/instance_resource.rb +11 -1
- data/lib/tms_client/link_header.rb +223 -0
- data/lib/tms_client/resource/collections.rb +13 -1
- data/lib/tms_client/resource/command.rb +3 -0
- data/lib/tms_client/resource/command_type.rb +1 -0
- data/lib/tms_client/resource/email_message.rb +52 -0
- data/lib/tms_client/resource/email_recipient.rb +13 -1
- data/lib/tms_client/resource/email_recipient_click.rb +8 -0
- data/lib/tms_client/resource/email_recipient_open.rb +8 -0
- data/lib/tms_client/resource/inbound_sms_message.rb +2 -1
- data/lib/tms_client/resource/keyword.rb +4 -0
- data/lib/tms_client/resource/recipient.rb +4 -2
- data/lib/tms_client/resource/sms_message.rb +21 -2
- data/lib/tms_client/resource/voice_message.rb +21 -1
- data/lib/tms_client/util/hal_link_parser.rb +5 -1
- data/lib/tms_client/version.rb +1 -1
- data/lib/tms_client.rb +6 -3
- data/spec/inbound_sms_messages_spec.rb +1 -1
- data/spec/instance_resource_spec.rb +32 -0
- data/tms_client.gemspec +0 -4
- metadata +10 -69
- data/lib/tms_client/resource/commands.rb +0 -3
- data/lib/tms_client/resource/email.rb +0 -9
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,94 +1,135 @@
|
|
1
1
|
TMS Client
|
2
2
|
===========
|
3
|
-
This is a
|
3
|
+
This is a reference Ruby client to interact with the GovDelivery TMS REST API.
|
4
4
|
|
5
5
|
Installation
|
6
6
|
------------
|
7
7
|
### Using Bundler
|
8
|
-
|
8
|
+
|
9
|
+
```ruby
|
9
10
|
gem 'tms_client'
|
10
11
|
```
|
11
12
|
|
12
13
|
### Standalone
|
14
|
+
|
13
15
|
```
|
14
16
|
$ gem install tms_client
|
15
17
|
```
|
16
18
|
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
``` ruby
|
23
|
-
client = TMS::Client.new('username', 'password', :api_root => 'https://tms.govdelivery.com')
|
20
|
+
Connecting
|
21
|
+
----------
|
22
|
+
Loading an instance of `TMS::Client` will automatically connect to the API to query the available resources for your account.
|
24
23
|
|
24
|
+
```ruby
|
25
|
+
client = TMS::Client.new('username', 'password', :api_root => 'https://stage-tms.govdelivery.com')
|
25
26
|
```
|
26
27
|
|
27
|
-
|
28
|
+
Messages
|
29
|
+
--------
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
client.sms_messages.
|
34
|
-
|
35
|
-
client.sms_messages.next.get #=> # more messages...
|
36
|
-
client.voice_messages.get #=> #lots of voice stuff
|
37
|
-
client.voice_messages.next #=> <TMS::Messages href=/messages/page/2 collection=[]>
|
38
|
-
# (if there is a second page)
|
39
|
-
client.voice_messages.next.get #=> # more messages...
|
31
|
+
### Loading messages
|
32
|
+
Sms, Email, and voice messages can be retrieved with the `get` collection method. Messages are paged in groups of 50. To retrieve another page, used the `next` method. This method will not be defined if another page is not available.
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
client.sms_messages.get # get the first page of sms messages
|
36
|
+
client.sms_messages.next.get # get the next page of sms messages
|
40
37
|
```
|
41
38
|
|
42
39
|
|
43
40
|
### Sending an SMS Message
|
44
41
|
|
45
|
-
```
|
46
|
-
message = client.sms_messages.build(:
|
42
|
+
```ruby
|
43
|
+
message = client.sms_messages.build(:body=>'Test Message!')
|
47
44
|
message.recipients.build(:phone=>'5551112222')
|
48
45
|
message.recipients.build(:phone=>'5551112223')
|
49
46
|
message.recipients.build # invalid - no phone
|
50
|
-
message.post
|
51
|
-
message.recipients.collection.detect{|r| r.errors }
|
47
|
+
message.post # true
|
48
|
+
message.recipients.collection.detect{|r| r.errors } # {"phone"=>["is not a number"]}
|
52
49
|
# save succeeded, but we have one bad recipient
|
53
|
-
message.href
|
54
|
-
message.get
|
50
|
+
message.href # "/messages/sms/87"
|
51
|
+
message.get # <TMS::SmsMessage href=/messages/sms/87 attributes={...}>
|
55
52
|
```
|
56
53
|
|
57
|
-
### Sending
|
54
|
+
### Sending Email
|
58
55
|
|
59
|
-
```
|
60
|
-
message = client.
|
56
|
+
```ruby
|
57
|
+
message = client.email_messages.build(:body=>'<p><a href="http://example.com">Visit here</a>', :subject => 'Hey')
|
58
|
+
message.recipients.build(:email=>'example1@example.com')
|
59
|
+
message.recipients.build(:email=>'example2@example.com')
|
60
|
+
message.post # true
|
61
|
+
message.recipients.collection.detect{|r| r.errors } # {"phone"=>["is not a number"]}
|
62
|
+
# save succeeded, but we have one bad recipient
|
63
|
+
message.href # "/messages/email/87"
|
64
|
+
message.get # <TMS::EmailMessage href=/messages/email/88 attributes={...}>
|
65
|
+
```
|
66
|
+
|
67
|
+
### Sending a Voice Message
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
message = client.voice_messages.build(:play_url=>'www.testmessage.com')
|
61
71
|
message.recipients.build(:phone=>'5551112222')
|
62
72
|
message.recipients.build(:phone=>'5551112223')
|
63
73
|
message.recipients.build # invalid - no phone
|
64
|
-
message.post
|
65
|
-
message.recipients.collection.detect{|r| r.errors }
|
74
|
+
message.post # true
|
75
|
+
message.recipients.collection.detect{|r| r.errors } # {"phone"=>["is not a number"]}
|
66
76
|
# save succeeded, but we have one bad recipient
|
67
|
-
message.href
|
68
|
-
message.get
|
77
|
+
message.href # "/messages/voice/87"
|
78
|
+
message.get # <TMS::VoiceMessage href=/messages/voice/87 attributes={...}>
|
79
|
+
```
|
80
|
+
|
81
|
+
Metrics
|
82
|
+
-------
|
83
|
+
### Viewing recipients that clicked on a link in an email
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
email_message.get
|
87
|
+
email_message.clicked.get
|
88
|
+
email_message.clicked.collection # => [<#EmailRecipient>,...]
|
69
89
|
```
|
70
90
|
|
91
|
+
### Viewing recipients that opened an email
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
email_message.get
|
95
|
+
email_message.opened.get
|
96
|
+
email_message.opened.collection # => [<#EmailRecipient>,...]
|
97
|
+
```
|
98
|
+
|
99
|
+
### Viewing a list of statistics for a recipient
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
email_recipient.clicks.get.collection #=> [<#EmailRecipientClick>,...]
|
103
|
+
|
104
|
+
email_recipient.opens.get.collection #=> [<#EmailRecipientOpen>,...]
|
105
|
+
```
|
106
|
+
|
107
|
+
Configuring 2-way SMS
|
108
|
+
---------------------
|
109
|
+
|
71
110
|
### Listing Command Types
|
111
|
+
Command Types are the available commands that can be used to respond to an incoming SMS message.
|
72
112
|
|
73
|
-
```
|
113
|
+
```ruby
|
74
114
|
command_types = client.command_types.get
|
75
115
|
command_types.collection.each do |at|
|
76
|
-
puts at.name
|
77
|
-
puts at.fields
|
116
|
+
puts at.name # "forward"
|
117
|
+
puts at.fields # ["url", "http_method", ...]
|
78
118
|
end
|
79
|
-
|
119
|
+
```
|
80
120
|
|
81
121
|
### Managing Keywords
|
122
|
+
Keywords are chunks of text that are used to match an incoming SMS message.
|
82
123
|
|
83
|
-
```
|
124
|
+
```ruby
|
84
125
|
# CRUD
|
85
126
|
keyword = client.keywords.build(:name => "BUSRIDE")
|
86
|
-
keyword.post
|
87
|
-
keyword.name
|
127
|
+
keyword.post # true
|
128
|
+
keyword.name # 'busride'
|
88
129
|
keyword.name = "TRAINRIDE"
|
89
|
-
keyword.put
|
90
|
-
keyword.name
|
91
|
-
keyword.delete
|
130
|
+
keyword.put # true
|
131
|
+
keyword.name # 'trainride'
|
132
|
+
keyword.delete # true
|
92
133
|
|
93
134
|
# list
|
94
135
|
keywords = client.keywords.get
|
@@ -98,11 +139,12 @@ end
|
|
98
139
|
```
|
99
140
|
|
100
141
|
### Managing Commands
|
142
|
+
Commands have a command type and one or more keywords. The example below configures the system to respond to an incoming SMS message containing the string "RIDE" (or "ride") by forwarding an http POST to `http://example.com/new_url`. The POST body variables are documented in GovDelivery's [TMS REST API documentation](https://govdelivery.atlassian.net/wiki/display/PM/TMS+Customer+API+Documentation#TMSCustomerAPIDocumentation-Configuring2-waySMS "GovDelivery TMS REST API").
|
101
143
|
|
102
144
|
```ruby
|
103
145
|
# CRUD
|
104
|
-
|
105
|
-
keyword
|
146
|
+
keyword = client.keywords.build(:name => "RIDE")
|
147
|
+
keyword.post
|
106
148
|
command = keyword.commands.build(
|
107
149
|
:name => "Forward to somewhere else",
|
108
150
|
:params => {:url => "http://example.com", :http_method => "get"},
|
@@ -120,21 +162,33 @@ end
|
|
120
162
|
```
|
121
163
|
|
122
164
|
|
123
|
-
|
124
|
-
|
125
|
-
|
165
|
+
Logging
|
166
|
+
-------
|
167
|
+
|
168
|
+
Any instance of a [Logger](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html "Ruby Logger")-like class can be passed in to the client; incoming and outgoing request information will then be logged to that instance.
|
126
169
|
|
127
|
-
The example below configures `TMS::Client` to log to STDOUT
|
170
|
+
The example below configures `TMS::Client` to log to `STDOUT`:
|
128
171
|
|
129
|
-
```
|
172
|
+
```ruby
|
130
173
|
logger = Logger.new(STDOUT)
|
131
174
|
client = TMS::Client.new('username', 'password', :logger => logger)
|
175
|
+
```
|
132
176
|
|
177
|
+
Generating Documentation
|
178
|
+
------------------------
|
179
|
+
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:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
# install development gems
|
183
|
+
bundle install
|
184
|
+
# generate documentation
|
185
|
+
rake yard
|
133
186
|
```
|
187
|
+
The generated documentation will be placed in the `doc` folder.
|
134
188
|
|
135
189
|
Compatibility
|
136
190
|
-------------
|
137
|
-
This project is tested and compatible with REE 1.8.7
|
191
|
+
This project is tested and compatible with REE 1.8.7, MRI 1.9.3, and MRI 2.0.0.
|
138
192
|
|
139
193
|
License
|
140
194
|
-------
|
data/Rakefile
CHANGED
data/lib/tms_client/base.rb
CHANGED
@@ -8,7 +8,7 @@ module TMS #:nodoc:
|
|
8
8
|
base.send(:extend, TMS::CoreExt)
|
9
9
|
end
|
10
10
|
|
11
|
-
attr_accessor :client, :href, :errors
|
11
|
+
attr_accessor :client, :href, :errors, :new_record
|
12
12
|
|
13
13
|
module ClassMethods
|
14
14
|
def to_param
|
@@ -21,6 +21,15 @@ module TMS #:nodoc:
|
|
21
21
|
self.client = client
|
22
22
|
self.href = href
|
23
23
|
end
|
24
|
+
|
25
|
+
def new_record?
|
26
|
+
!!self.new_record
|
27
|
+
end
|
28
|
+
|
29
|
+
def href=(href)
|
30
|
+
self.new_record=false
|
31
|
+
@href=href
|
32
|
+
end
|
24
33
|
end
|
25
34
|
|
26
35
|
end
|
data/lib/tms_client/client.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
# The client class to connect and talk to the TMS REST API.
|
1
|
+
# The client class to connect and talk to the TMS REST API.
|
2
2
|
class TMS::Client
|
3
3
|
include TMS::Util::HalLinkParser
|
4
4
|
include TMS::CoreExt
|
5
5
|
|
6
6
|
attr_accessor :connection, :href
|
7
7
|
|
8
|
-
# Create a new client and issue a request for the available resources for a given account.
|
8
|
+
# Create a new client and issue a request for the available resources for a given account.
|
9
9
|
#
|
10
10
|
# === Options
|
11
11
|
# * +:api_root+ - The root URL of the TMS api. Defaults to localhost:3000
|
@@ -13,9 +13,9 @@ class TMS::Client
|
|
13
13
|
#
|
14
14
|
# === Examples
|
15
15
|
# client = TMS::Client.new("foo@example.com", "onetwothree", {
|
16
|
-
# :api_root => "https://tms.govdelivery.com",
|
16
|
+
# :api_root => "https://tms.govdelivery.com",
|
17
17
|
# :logger => Logger.new(STDOUT)})
|
18
|
-
#
|
18
|
+
#
|
19
19
|
def initialize(username, password, options = {:api_root => 'http://localhost:3000', :logger => nil})
|
20
20
|
@api_root = options[:api_root]
|
21
21
|
connect!(username, password, options[:logger])
|
@@ -34,12 +34,12 @@ class TMS::Client
|
|
34
34
|
def get(href)
|
35
35
|
response = raw_connection.get(href)
|
36
36
|
case response.status
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
when 401..499
|
38
|
+
raise TMS::Request::Error.new(response.status)
|
39
|
+
when 202
|
40
|
+
raise TMS::Request::InProgress.new(response.body['message'])
|
41
|
+
else
|
42
|
+
return response
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -62,10 +62,10 @@ class TMS::Client
|
|
62
62
|
def delete(href)
|
63
63
|
response = raw_connection.delete(href)
|
64
64
|
case response.status
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
when 200
|
66
|
+
return response
|
67
|
+
else
|
68
|
+
raise TMS::Request::Error.new(response.status)
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
@@ -77,4 +77,4 @@ class TMS::Client
|
|
77
77
|
self
|
78
78
|
end
|
79
79
|
|
80
|
-
end
|
80
|
+
end
|
@@ -30,6 +30,7 @@ module TMS::CollectionResource
|
|
30
30
|
|
31
31
|
def build(attributes=nil)
|
32
32
|
thing = instance_class(self.class).new(client, self.href, attributes || {})
|
33
|
+
thing.new_record = true
|
33
34
|
self.collection << thing
|
34
35
|
thing
|
35
36
|
end
|
@@ -50,4 +51,4 @@ module TMS::CollectionResource
|
|
50
51
|
end
|
51
52
|
end
|
52
53
|
end
|
53
|
-
end
|
54
|
+
end
|
@@ -88,15 +88,18 @@ module TMS::InstanceResource
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def get
|
91
|
+
raise TMS::Errors::InvalidGet if self.new_record?
|
91
92
|
set_attributes_from_hash(self.client.get(href).body)
|
92
93
|
self
|
93
94
|
end
|
94
95
|
|
95
96
|
def post
|
96
97
|
response = client.post(self)
|
98
|
+
|
97
99
|
case response.status
|
98
|
-
when
|
100
|
+
when 200..299
|
99
101
|
set_attributes_from_hash(response.body)
|
102
|
+
self.new_record=false
|
100
103
|
return true
|
101
104
|
when 401
|
102
105
|
raise Exception.new("401 Not Authorized")
|
@@ -114,6 +117,7 @@ module TMS::InstanceResource
|
|
114
117
|
response = client.put(self)
|
115
118
|
case response.status
|
116
119
|
when 200
|
120
|
+
self.new_record=false
|
117
121
|
set_attributes_from_hash(response.body)
|
118
122
|
return true
|
119
123
|
when 401
|
@@ -156,6 +160,12 @@ module TMS::InstanceResource
|
|
156
160
|
json_hash
|
157
161
|
end
|
158
162
|
|
163
|
+
protected
|
164
|
+
|
165
|
+
def relation_class(rel)
|
166
|
+
self.class.custom_class_names[rel.to_sym] || super
|
167
|
+
end
|
168
|
+
|
159
169
|
private
|
160
170
|
|
161
171
|
def set_attributes_from_hash(hash)
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# link_header, Copyright (c) 2009 Mike Burrows
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require "strscan"
|
23
|
+
|
24
|
+
#
|
25
|
+
# Represents an HTTP link header of the form described in the draft spec http://tools.ietf.org/id/draft-nottingham-http-link-header-06.txt.
|
26
|
+
# It is simply a list of LinkHeader::Link objects and some conversion functions.
|
27
|
+
#
|
28
|
+
class LinkHeader
|
29
|
+
|
30
|
+
# An array of Link objects
|
31
|
+
attr_reader :links
|
32
|
+
|
33
|
+
#
|
34
|
+
# Initialize from a collection of either LinkHeader::Link objects or data from which Link objects can be created.
|
35
|
+
#
|
36
|
+
# From a list of LinkHeader::Link objects:
|
37
|
+
#
|
38
|
+
# LinkHeader.new([
|
39
|
+
# LinkHeader::Link.new("http://example.com/foo", [["rel", "self"]]),
|
40
|
+
# LinkHeader::Link.new("http://example.com/", [["rel", "up"]])])
|
41
|
+
#
|
42
|
+
# From the equivalent JSON-friendly raw data:
|
43
|
+
#
|
44
|
+
# LinkHeader.new([
|
45
|
+
# ["http://example.com/foo", [["rel", "self"]]],
|
46
|
+
# ["http://example.com/", [["rel", "up"]]]]).to_s
|
47
|
+
#
|
48
|
+
# See also LinkHeader.parse
|
49
|
+
#
|
50
|
+
def initialize(links=[])
|
51
|
+
if links
|
52
|
+
@links = links.map{|l| l.kind_of?(Link) ? l : Link.new(*l)}
|
53
|
+
else
|
54
|
+
@links = []
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Convert to a JSON-friendly array
|
60
|
+
#
|
61
|
+
# LinkHeader.parse('<http://example.com/foo>; rel="self", <http://example.com/>; rel = "up"').to_a
|
62
|
+
# #=> [["http://example.com/foo", [["rel", "self"]]],
|
63
|
+
# ["http://example.com/", [["rel", "up"]]]]
|
64
|
+
#
|
65
|
+
def to_a
|
66
|
+
links.map{|l| l.to_a}
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Convert to string representation as per the link header spec
|
71
|
+
#
|
72
|
+
# LinkHeader.new([
|
73
|
+
# ["http://example.com/foo", [["rel", "self"]]],
|
74
|
+
# ["http://example.com/", [["rel", "up"]]]]).to_s
|
75
|
+
# #=> '<http://example.com/foo>; rel="self", <http://example.com/>; rel = "up"'
|
76
|
+
#
|
77
|
+
def to_s
|
78
|
+
links.join(', ')
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Regexes for link header parsing. TOKEN and QUOTED in particular should conform to RFC2616.
|
83
|
+
#
|
84
|
+
# Acknowledgement: The QUOTED regexp is based on
|
85
|
+
# http://stackoverflow.com/questions/249791/regexp-for-quoted-string-with-escaping-quotes/249937#249937
|
86
|
+
#
|
87
|
+
HREF = / *< *([^>]*) *> *;? */ #:nodoc: note: no attempt to check URI validity
|
88
|
+
TOKEN = /([^()<>@,;:\"\[\]?={}\s]+)/ #:nodoc: non-empty sequence of non-separator characters
|
89
|
+
QUOTED = /"((?:[^"\\]|\\.)*)"/ #:nodoc: double-quoted strings with backslash-escaped double quotes
|
90
|
+
ATTR = /#{TOKEN} *= *(#{TOKEN}|#{QUOTED}) */ #:nodoc:
|
91
|
+
SEMI = /; */ #:nodoc:
|
92
|
+
COMMA = /, */ #:nodoc:
|
93
|
+
|
94
|
+
#
|
95
|
+
# Parse a link header, returning a new LinkHeader object
|
96
|
+
#
|
97
|
+
# LinkHeader.parse('<http://example.com/foo>; rel="self", <http://example.com/>; rel = "up"').to_a
|
98
|
+
# #=> [["http://example.com/foo", [["rel", "self"]]],
|
99
|
+
# ["http://example.com/", [["rel", "up"]]]]
|
100
|
+
#
|
101
|
+
def self.parse(link_header)
|
102
|
+
return new unless link_header
|
103
|
+
|
104
|
+
scanner = StringScanner.new(link_header)
|
105
|
+
links = []
|
106
|
+
while scanner.scan(HREF)
|
107
|
+
href = scanner[1]
|
108
|
+
attrs = []
|
109
|
+
while scanner.scan(ATTR)
|
110
|
+
attr_name, token, quoted = scanner[1], scanner[3], scanner[4].gsub(/\\"/, '"')
|
111
|
+
attrs.push([attr_name, token || quoted])
|
112
|
+
break unless scanner.scan(SEMI)
|
113
|
+
end
|
114
|
+
links.push(Link.new(href, attrs))
|
115
|
+
break unless scanner.scan(COMMA)
|
116
|
+
end
|
117
|
+
|
118
|
+
new(links)
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Find a member link that has the given attributes
|
123
|
+
#
|
124
|
+
def find_link(*attr_pairs)
|
125
|
+
links.detect do |link|
|
126
|
+
!attr_pairs.detect do |pair|
|
127
|
+
!link.attr_pairs.include?(pair)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Render as a list of HTML link elements
|
134
|
+
#
|
135
|
+
def to_html(separator="\n")
|
136
|
+
links.map{|link| link.to_html}.join(separator)
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Represents a link - an href and a list of attributes (key value pairs)
|
141
|
+
#
|
142
|
+
# LinkHeader::Link.new("http://example.com/foo", [["rel", "self"]]).to_s
|
143
|
+
# => '<http://example.com/foo>; rel="self"'
|
144
|
+
#
|
145
|
+
class Link
|
146
|
+
#
|
147
|
+
# The link's URI string
|
148
|
+
#
|
149
|
+
# LinkHeader::Link.new("http://example.com/foo", [["rel", "self"]]).href
|
150
|
+
# => 'http://example.com/foo>'
|
151
|
+
#
|
152
|
+
attr_reader :href
|
153
|
+
|
154
|
+
#
|
155
|
+
# The link's attributes, an array of key-value pairs
|
156
|
+
#
|
157
|
+
# LinkHeader::Link.new("http://example.com/foo", [["rel", "self"], ["rel", "canonical"]]).attr_pairs
|
158
|
+
# => [["rel", "self"], ["rel", "canonical"]]
|
159
|
+
#
|
160
|
+
attr_reader :attr_pairs
|
161
|
+
|
162
|
+
#
|
163
|
+
# Initialize a Link from an href and attribute list
|
164
|
+
#
|
165
|
+
# LinkHeader::Link.new("http://example.com/foo", [["rel", "self"]]).to_s
|
166
|
+
# => '<http://example.com/foo>; rel="self"'
|
167
|
+
#
|
168
|
+
def initialize(href, attr_pairs)
|
169
|
+
@href, @attr_pairs = href, attr_pairs
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Lazily convert the attribute list to a Hash
|
174
|
+
#
|
175
|
+
# Beware repeated attribute names (it's safer to use #attr_pairs if this is risk):
|
176
|
+
#
|
177
|
+
# LinkHeader::Link.new("http://example.com/foo", [["rel", "self"], ["rel", "canonical"]]).attrs
|
178
|
+
# => {"rel" =>"canonical"}
|
179
|
+
#
|
180
|
+
def attrs
|
181
|
+
@attrs ||= Hash[*attr_pairs.flatten]
|
182
|
+
end
|
183
|
+
|
184
|
+
#
|
185
|
+
# Access #attrs by key
|
186
|
+
#
|
187
|
+
def [](key)
|
188
|
+
attrs[key]
|
189
|
+
end
|
190
|
+
|
191
|
+
#
|
192
|
+
# Convert to a JSON-friendly Array
|
193
|
+
#
|
194
|
+
# LinkHeader::Link.new("http://example.com/foo", [["rel", "self"], ["rel", "canonical"]]).to_a
|
195
|
+
# => ["http://example.com/foo", [["rel", "self"], ["rel", "canonical"]]]
|
196
|
+
#
|
197
|
+
def to_a
|
198
|
+
[href, attr_pairs]
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# Convert to string representation as per the link header spec. This includes backspace-escaping doublequote characters in
|
203
|
+
# quoted attribute values.
|
204
|
+
#
|
205
|
+
# Convert to string representation as per the link header spec
|
206
|
+
#
|
207
|
+
# LinkHeader::Link.new(["http://example.com/foo", [["rel", "self"]]]).to_s
|
208
|
+
# #=> '<http://example.com/foo>; rel="self"'
|
209
|
+
#
|
210
|
+
def to_s
|
211
|
+
(["<#{href}>"] + attr_pairs.map{|k, v| "#{k}=\"#{v.gsub(/"/, '\"')}\""}).join('; ')
|
212
|
+
end
|
213
|
+
|
214
|
+
#
|
215
|
+
# Bonus! Render as an HTML link element
|
216
|
+
#
|
217
|
+
# LinkHeader::Link.new(["http://example.com/foo", [["rel", "self"]]]).to_html
|
218
|
+
# #=> '<link href="http://example.com/foo" rel="self">'
|
219
|
+
def to_html
|
220
|
+
([%Q(<link href="#{href}")] + attr_pairs.map{|k, v| "#{k}=\"#{v.gsub(/"/, '\"')}\""}).join(' ')
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -10,7 +10,7 @@ class TMS::SmsMessages
|
|
10
10
|
include TMS::CollectionResource
|
11
11
|
end
|
12
12
|
|
13
|
-
class TMS::
|
13
|
+
class TMS::EmailMessages
|
14
14
|
include TMS::CollectionResource
|
15
15
|
end
|
16
16
|
|
@@ -22,6 +22,14 @@ class TMS::EmailRecipients
|
|
22
22
|
include TMS::CollectionResource
|
23
23
|
end
|
24
24
|
|
25
|
+
class TMS::EmailRecipientOpens
|
26
|
+
include TMS::CollectionResource
|
27
|
+
end
|
28
|
+
|
29
|
+
class TMS::EmailRecipientClicks
|
30
|
+
include TMS::CollectionResource
|
31
|
+
end
|
32
|
+
|
25
33
|
# A collection of Keyword objects.
|
26
34
|
#
|
27
35
|
# === Example
|
@@ -47,3 +55,7 @@ end
|
|
47
55
|
class TMS::CommandTypes
|
48
56
|
include TMS::CollectionResource
|
49
57
|
end
|
58
|
+
|
59
|
+
class TMS::Commands
|
60
|
+
include TMS::CollectionResource
|
61
|
+
end
|
@@ -17,7 +17,10 @@ module TMS #:nodoc:
|
|
17
17
|
class Command
|
18
18
|
include InstanceResource
|
19
19
|
|
20
|
+
# @!parse attr_accessor :name, :command_type, :params
|
20
21
|
writeable_attributes :name, :command_type, :params
|
22
|
+
|
23
|
+
# @!parse attr_reader :created_at, :updated_at
|
21
24
|
readonly_attributes :created_at, :updated_at
|
22
25
|
|
23
26
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module TMS #:nodoc:
|
2
|
+
# An EmailMessage is used to create and send a email to a collection of EmailRecipient
|
3
|
+
# objects. Certain metrics are available after the email is sent, including
|
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
|
+
#
|
13
|
+
#
|
14
|
+
# === Example
|
15
|
+
# Sending a message
|
16
|
+
# email_message = client.email_messages.build(:subject => "Great news!", :body => "You win! <a href='http://example.com/'>click here</a>.")
|
17
|
+
# email_message.recipients.build(:email => "john@example.com")
|
18
|
+
# email_message.recipients.build(:email => "jeff@example.com")
|
19
|
+
# email_message.post
|
20
|
+
# email_message.get
|
21
|
+
#
|
22
|
+
# Viewing recipients that clicked on a link in the email
|
23
|
+
# email_message.get
|
24
|
+
# email_message.clicked.get
|
25
|
+
# email_message.clicked.collection # => [<#EmailRecipient>,...]
|
26
|
+
#
|
27
|
+
# Viewing recipients that opened the email
|
28
|
+
# email_message.get
|
29
|
+
# email_message.opened.get
|
30
|
+
# email_message.opened.collection # => [<#EmailRecipient>,...]
|
31
|
+
class EmailMessage
|
32
|
+
include InstanceResource
|
33
|
+
|
34
|
+
# @!parse attr_accessor :body, :from_name, :subject
|
35
|
+
writeable_attributes :body, :from_name, :subject
|
36
|
+
|
37
|
+
# @!parse attr_reader :created_at, :status
|
38
|
+
readonly_attributes :created_at, :status
|
39
|
+
|
40
|
+
##
|
41
|
+
# A CollectionResource of EmailRecipients on this email
|
42
|
+
collection_attribute :recipients, 'EmailRecipients'
|
43
|
+
|
44
|
+
##
|
45
|
+
# A CollectionResource of EmailRecipients that opened this email
|
46
|
+
collection_attribute :opened, 'EmailRecipients'
|
47
|
+
|
48
|
+
##
|
49
|
+
# A CollectionResource of EmailRecipients that clicked on at least one link in this email
|
50
|
+
collection_attribute :clicked, 'EmailRecipients'
|
51
|
+
end
|
52
|
+
end
|
@@ -2,6 +2,18 @@ module TMS #:nodoc:
|
|
2
2
|
class EmailRecipient
|
3
3
|
include InstanceResource
|
4
4
|
|
5
|
+
# @!parse attr_accessor :email
|
5
6
|
writeable_attributes :email
|
7
|
+
|
8
|
+
# @!parse attr_reader :completed_at
|
9
|
+
readonly_attributes :completed_at
|
10
|
+
|
11
|
+
##
|
12
|
+
# A CollectionResource of EmailRecipientOpens for this EmailRecipient
|
13
|
+
collection_attribute :opens, 'EmailRecipientOpens'
|
14
|
+
|
15
|
+
##
|
16
|
+
# A CollectionResource of EmailRecipientClicks for this EmailRecipient
|
17
|
+
collection_attribute :clicks, 'EmailRecipientClicks'
|
6
18
|
end
|
7
|
-
end
|
19
|
+
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module TMS #:nodoc:
|
2
2
|
class Recipient
|
3
3
|
include InstanceResource
|
4
|
-
|
4
|
+
# @!parse attr_accessor :phone
|
5
5
|
writeable_attributes :phone
|
6
|
-
|
6
|
+
|
7
|
+
# @!parse attr_reader :formatted_phone, :error_message, :status, :completed_at
|
8
|
+
readonly_attributes :formatted_phone, :error_message, :status, :completed_at
|
7
9
|
end
|
8
10
|
end
|
@@ -1,9 +1,28 @@
|
|
1
1
|
module TMS #:nodoc:
|
2
|
+
# An SMSMessage is used to create and send a text message to a collection of Recipient
|
3
|
+
# objects.
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# ==== Attributes
|
7
|
+
#
|
8
|
+
# * +body+ - The content of the SMS. This field will be truncated to 160 characters.
|
9
|
+
#
|
10
|
+
# === Example
|
11
|
+
# sms = client.sms_messages.build(:body => "Hello")
|
12
|
+
# sms.recipients.build(:phone => "+18001002000")
|
13
|
+
# sms.post
|
14
|
+
# sms.get
|
2
15
|
class SmsMessage
|
3
16
|
include InstanceResource
|
17
|
+
|
18
|
+
# @!parse attr_accessor :body
|
19
|
+
writeable_attributes :body
|
4
20
|
|
5
|
-
|
6
|
-
readonly_attributes :created_at, :completed_at
|
21
|
+
# @!parse attr_reader :created_at, :completed_at, :status
|
22
|
+
readonly_attributes :created_at, :completed_at, :status
|
23
|
+
|
24
|
+
##
|
25
|
+
# A CollectionResource of Recipient objects
|
7
26
|
collection_attributes :recipients
|
8
27
|
end
|
9
28
|
end
|
@@ -1,9 +1,29 @@
|
|
1
1
|
module TMS #:nodoc:
|
2
|
+
# A VoiceMessage is used to create and send a voice message to a collection of Recipient
|
3
|
+
# objects. The recipients are called and the provided +play_url+ is
|
4
|
+
# played to them. Accepted sound formats include +wav+, +mp3+, and +aiff+.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# ==== Attributes
|
8
|
+
#
|
9
|
+
# * +play_url+ - The url to the sound file to be played back to the call recipients
|
10
|
+
#
|
11
|
+
# === Example
|
12
|
+
# voice_message = client.voice_messages.build(:play_url => "http://example.com/emergency_weather.mp3")
|
13
|
+
# voice_message.recipients.build(:phone => "+18001002000")
|
14
|
+
# voice_message.post
|
15
|
+
# voice_message.get
|
2
16
|
class VoiceMessage
|
3
17
|
include InstanceResource
|
4
18
|
|
19
|
+
# @!parse attr_accessor :play_url
|
5
20
|
writeable_attributes :play_url
|
6
|
-
|
21
|
+
|
22
|
+
# @!parse attr_reader :created_at, :completed_at, :status
|
23
|
+
readonly_attributes :created_at, :completed_at, :status
|
24
|
+
|
25
|
+
##
|
26
|
+
# A CollectionResource of Recipient objects
|
7
27
|
collection_attributes :recipients
|
8
28
|
|
9
29
|
def self.to_s
|
@@ -29,7 +29,7 @@ module TMS::Util
|
|
29
29
|
if rel == 'self'
|
30
30
|
self.href = href
|
31
31
|
else
|
32
|
-
klass =
|
32
|
+
klass = relation_class(rel)
|
33
33
|
klass = self.class if ['first', 'prev', 'next', 'last'].include?(rel)
|
34
34
|
if klass
|
35
35
|
subresources[rel] = klass.new(self.client, href)
|
@@ -42,6 +42,10 @@ module TMS::Util
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
def relation_class(rel)
|
46
|
+
::TMS.const_get(classify(rel)) rescue nil
|
47
|
+
end
|
48
|
+
|
45
49
|
def setup_subresource(link)
|
46
50
|
return unless link
|
47
51
|
link.each { |rel, href| self.metaclass.send(:define_method, rel.to_sym, &lambda { subresources[rel] }) unless rel == 'self' }
|
data/lib/tms_client/version.rb
CHANGED
data/lib/tms_client.rb
CHANGED
@@ -4,9 +4,9 @@ end
|
|
4
4
|
require 'active_support'
|
5
5
|
require 'tms_client/version'
|
6
6
|
require 'faraday'
|
7
|
-
require 'link_header'
|
8
7
|
require 'faraday_middleware'
|
9
8
|
|
9
|
+
require 'tms_client/link_header'
|
10
10
|
require 'tms_client/util/hal_link_parser'
|
11
11
|
require 'tms_client/util/core_ext'
|
12
12
|
require 'tms_client/connection'
|
@@ -16,15 +16,18 @@ require 'tms_client/base'
|
|
16
16
|
require 'tms_client/instance_resource'
|
17
17
|
require 'tms_client/collection_resource'
|
18
18
|
require 'tms_client/request'
|
19
|
+
require 'tms_client/errors'
|
19
20
|
|
20
21
|
require 'tms_client/resource/collections'
|
21
22
|
require 'tms_client/resource/recipient'
|
22
23
|
require 'tms_client/resource/email_recipient'
|
24
|
+
require 'tms_client/resource/email_recipient_open'
|
25
|
+
require 'tms_client/resource/email_recipient_click'
|
23
26
|
require 'tms_client/resource/sms_message'
|
24
27
|
require 'tms_client/resource/voice_message'
|
28
|
+
require 'tms_client/resource/email_message'
|
25
29
|
require 'tms_client/resource/inbound_sms_message'
|
26
30
|
require 'tms_client/resource/command_type'
|
27
31
|
require 'tms_client/resource/command'
|
28
|
-
require 'tms_client/resource/commands'
|
29
32
|
require 'tms_client/resource/keyword'
|
30
|
-
|
33
|
+
|
@@ -6,7 +6,7 @@ describe TMS::InboundSmsMessages do
|
|
6
6
|
double('client')
|
7
7
|
end
|
8
8
|
before do
|
9
|
-
@messages = TMS::InboundSmsMessages.new(client, '/
|
9
|
+
@messages = TMS::InboundSmsMessages.new(client, '/inbound_messages')
|
10
10
|
end
|
11
11
|
it 'should GET itself' do
|
12
12
|
body = [{:body=>"HELP", :from=>"+16125551212", :created_at=>"a while ago", :to=>"(651) 433-6258"}, {:body=>"STOP", :from=>"+16125551212", :created_at=>"a while ago", :to=>"(651) 433-6258"}]
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Foo
|
3
|
+
include TMS::InstanceResource
|
4
|
+
writeable_attributes :bar
|
5
|
+
collection_attribute :blah, 'EmailMessage'
|
6
|
+
end
|
7
|
+
|
8
|
+
describe TMS::InstanceResource do
|
9
|
+
context "creating a new inbound messages list" do
|
10
|
+
let(:happy_response) do
|
11
|
+
double(:status => 201, :body => {})
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:client) do
|
15
|
+
double('client', :post => happy_response)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
before do
|
20
|
+
@instance_resource = Foo.new(client)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should POST' do
|
24
|
+
@instance_resource.bar = "OMG"
|
25
|
+
@instance_resource.post.should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should correctly reflect on collection resources' do
|
29
|
+
@instance_resource.blah.class.should == TMS::EmailMessage
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/tms_client.gemspec
CHANGED
@@ -19,10 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.add_runtime_dependency "faraday"
|
20
20
|
s.add_runtime_dependency "faraday_middleware"
|
21
21
|
s.add_runtime_dependency "activesupport"
|
22
|
-
s.add_runtime_dependency "link_header"
|
23
|
-
s.add_development_dependency "rspec"
|
24
|
-
s.add_development_dependency "rake"
|
25
|
-
s.add_development_dependency "rubygems-tasks"
|
26
22
|
|
27
23
|
s.files = %w{
|
28
24
|
Gemfile
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tms_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-02-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -59,70 +59,6 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: link_header
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
|
-
requirements:
|
67
|
-
- - ! '>='
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
type: :runtime
|
71
|
-
prerelease: false
|
72
|
-
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - ! '>='
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '0'
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
|
-
name: rspec
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
|
-
requirements:
|
83
|
-
- - ! '>='
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: '0'
|
86
|
-
type: :development
|
87
|
-
prerelease: false
|
88
|
-
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - ! '>='
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: '0'
|
94
|
-
- !ruby/object:Gem::Dependency
|
95
|
-
name: rake
|
96
|
-
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
|
-
requirements:
|
99
|
-
- - ! '>='
|
100
|
-
- !ruby/object:Gem::Version
|
101
|
-
version: '0'
|
102
|
-
type: :development
|
103
|
-
prerelease: false
|
104
|
-
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
|
-
requirements:
|
107
|
-
- - ! '>='
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '0'
|
110
|
-
- !ruby/object:Gem::Dependency
|
111
|
-
name: rubygems-tasks
|
112
|
-
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
|
-
requirements:
|
115
|
-
- - ! '>='
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
|
-
requirements:
|
123
|
-
- - ! '>='
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: '0'
|
126
62
|
description: ! 'A reference implementation, written in Ruby, to interact with GovDelivery''s
|
127
63
|
TMS API. The client is compatible with Ruby versions 1.8.7 and 1.9.3. '
|
128
64
|
email:
|
@@ -139,15 +75,18 @@ files:
|
|
139
75
|
- lib/tms_client/client.rb
|
140
76
|
- lib/tms_client/collection_resource.rb
|
141
77
|
- lib/tms_client/connection.rb
|
78
|
+
- lib/tms_client/errors.rb
|
142
79
|
- lib/tms_client/instance_resource.rb
|
80
|
+
- lib/tms_client/link_header.rb
|
143
81
|
- lib/tms_client/logger.rb
|
144
82
|
- lib/tms_client/request.rb
|
145
83
|
- lib/tms_client/resource/collections.rb
|
146
84
|
- lib/tms_client/resource/command.rb
|
147
85
|
- lib/tms_client/resource/command_type.rb
|
148
|
-
- lib/tms_client/resource/
|
149
|
-
- lib/tms_client/resource/email.rb
|
86
|
+
- lib/tms_client/resource/email_message.rb
|
150
87
|
- lib/tms_client/resource/email_recipient.rb
|
88
|
+
- lib/tms_client/resource/email_recipient_click.rb
|
89
|
+
- lib/tms_client/resource/email_recipient_open.rb
|
151
90
|
- lib/tms_client/resource/inbound_sms_message.rb
|
152
91
|
- lib/tms_client/resource/keyword.rb
|
153
92
|
- lib/tms_client/resource/recipient.rb
|
@@ -160,6 +99,7 @@ files:
|
|
160
99
|
- spec/client_spec.rb
|
161
100
|
- spec/command_types_spec.rb
|
162
101
|
- spec/inbound_sms_messages_spec.rb
|
102
|
+
- spec/instance_resource_spec.rb
|
163
103
|
- spec/keyword_spec.rb
|
164
104
|
- spec/keywords_spec.rb
|
165
105
|
- spec/message_spec.rb
|
@@ -186,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
126
|
version: '0'
|
187
127
|
requirements: []
|
188
128
|
rubyforge_project:
|
189
|
-
rubygems_version: 1.8.
|
129
|
+
rubygems_version: 1.8.25
|
190
130
|
signing_key:
|
191
131
|
specification_version: 3
|
192
132
|
summary: A ruby client to interact with the GovDelivery TMS REST API.
|
@@ -194,6 +134,7 @@ test_files:
|
|
194
134
|
- spec/client_spec.rb
|
195
135
|
- spec/command_types_spec.rb
|
196
136
|
- spec/inbound_sms_messages_spec.rb
|
137
|
+
- spec/instance_resource_spec.rb
|
197
138
|
- spec/keyword_spec.rb
|
198
139
|
- spec/keywords_spec.rb
|
199
140
|
- spec/message_spec.rb
|