pomade 0.2.0 → 0.2.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.
- data/lib/pomade.rb +8 -2
- data/lib/pomade/exceptions.rb +19 -0
- data/lib/pomade/publisher.rb +165 -66
- data/lib/pomade/version.rb +1 -1
- data/readme.md +33 -20
- metadata +3 -2
data/lib/pomade.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Pomade
|
2
|
+
# Errors thrown from Pomegranate
|
3
|
+
class ResponseError < StandardError; end
|
4
|
+
|
5
|
+
# If an asset's keys do not match up
|
6
|
+
class InvalidAssetKeys < StandardError; end
|
7
|
+
|
8
|
+
# If an asset's type is invalid
|
9
|
+
class InvalidAssetType < StandardError; end
|
10
|
+
|
11
|
+
# If a URL's response is not OK
|
12
|
+
class BadAssetValueURL < StandardError; end
|
13
|
+
|
14
|
+
# If an :image asset's value is not a valid URL
|
15
|
+
class InvalidImageValue < StandardError; end
|
16
|
+
|
17
|
+
# If an :video asset's value is not a valid URL
|
18
|
+
class InvalidVideoValue < StandardError; end
|
19
|
+
end
|
data/lib/pomade/publisher.rb
CHANGED
@@ -3,35 +3,30 @@ require "ntlm/http"
|
|
3
3
|
require "nokogiri"
|
4
4
|
|
5
5
|
module Pomade
|
6
|
+
##
|
7
|
+
# Handles all interactions to Pomegranate.
|
6
8
|
class Publisher
|
7
|
-
|
8
|
-
# Pomegranate.
|
9
|
+
##
|
10
|
+
# Creates a new instance of +Publisher+ that pushes records to Pomegranate.
|
9
11
|
#
|
10
|
-
# Parameters
|
11
|
-
#
|
12
|
-
# subdomain - [string] The subdomain for the Pomegranate instance that
|
13
|
-
# you'd like to connect to.
|
14
|
-
# username - [string] The username used for connecting to your instance.
|
15
|
-
# password - [string] The password used for connecting to your instance.
|
16
|
-
# client_id - [string] Your client ID.
|
17
|
-
# opts - [hash] (optional) Additional options.
|
18
|
-
#
|
19
|
-
# Available options are:
|
20
|
-
# host - [string] The host (domain name) that Pomegranate lives on.
|
21
|
-
# pathname - [string] The path that is used for interacting with
|
22
|
-
# Pomegranate.
|
23
|
-
# time_format - [string] (strftime) change the layout of the timestamp
|
24
|
-
# domain - [string] NTLM login domain.
|
25
|
-
# debug - [boolean] Turns on debug mode. This will print out
|
26
|
-
# any activity.
|
27
|
-
#
|
28
|
-
# Returns:
|
12
|
+
# == Parameters
|
29
13
|
#
|
30
|
-
#
|
14
|
+
# * *subdomain* _(string)_ - The subdomain for the Pomegranate instance that you'd like to connect to.
|
15
|
+
# * *username* _(string)_ - The username used for connecting to your isntance.
|
16
|
+
# * *password* _(string)_ - The password used for connecting to your isntance.
|
17
|
+
# * *client_id* _(string)_ - Your client ID.
|
18
|
+
# * *opts* _(hash, optional)_ - Additional options. Available options are:
|
19
|
+
# * *:host* _(string)_ - The host (domain name) that your Pomegranate instance lives on.
|
20
|
+
# * *:pathname* _(string)_ - The path that is used for interacting with assets.
|
21
|
+
# * *:time_format* _(strftime)_ - Change the layout of the timestamp that is posted to your instance.
|
22
|
+
# * *:domain* _(string)_ - NTLM login domain.
|
31
23
|
#
|
32
|
-
#
|
24
|
+
# == Returns
|
25
|
+
# An instance of +Pomade::Publisher+
|
33
26
|
#
|
34
|
-
#
|
27
|
+
# == Example
|
28
|
+
#
|
29
|
+
# @pom = Pomade::Publisher.new('my-subdomain', 'myusername', 'mypassword', 'XX')
|
35
30
|
def initialize(subdomain, username, password, client_id, opts = {})
|
36
31
|
@subdomain = subdomain
|
37
32
|
@username = username
|
@@ -44,42 +39,64 @@ module Pomade
|
|
44
39
|
@options[:pathname] = opts[:pathname] || '/p/p.svc/Assets/'
|
45
40
|
@options[:time_format] = opts[:time_format] || "%Y-%m-%dT%H:%M:%SZ"
|
46
41
|
@options[:domain] = opts[:domain] || nil
|
47
|
-
@options[:debug] = opts[:debug] || false
|
48
42
|
end
|
49
43
|
|
50
|
-
|
44
|
+
##
|
45
|
+
# Publishes an array of assets to Pomegranate and returns the results in a +hash+.
|
51
46
|
#
|
52
|
-
# Parameters
|
47
|
+
# == Parameters
|
53
48
|
#
|
54
|
-
#
|
55
|
-
#
|
49
|
+
# * *assets* _(array)_ - A collection of assets. Each item consists of a hash with three keys: +:target+, +:type+ and +:value+. The values for keys +:target+ and +:value+ are both strings while the +:type+ key's value is a symbol. Available values are:
|
50
|
+
# * +:image+ -- An +IMAGE+ type asset
|
51
|
+
# * +:video+ -- A +VIDEO+ type asset
|
52
|
+
# * +:text+ -- A +TEXT+ type asset
|
56
53
|
#
|
57
|
-
# Returns
|
54
|
+
# == Returns
|
58
55
|
#
|
59
|
-
#
|
56
|
+
# A +hash+ containing two keys: +record_id+ and +assets+.
|
60
57
|
#
|
61
|
-
# Example
|
58
|
+
# == Example
|
62
59
|
#
|
63
60
|
# records = [
|
64
|
-
# { target: "XX~
|
65
|
-
# { target: "XX~
|
61
|
+
# { target: "XX~username", type: :text, value: "jakebellacera"},
|
62
|
+
# { target: "XX~avatar", type: :image, value: "http://www.gravatar.com/avatar/98363013aa1237798130bc0fd2c4159d.png"}
|
66
63
|
# ]
|
64
|
+
#
|
67
65
|
# @pom.publish(records)
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
66
|
+
# # =>
|
67
|
+
# {
|
68
|
+
# record_id: "XX-91c8071a-1201-4f99-bc9d-f8d53a947dc1",
|
69
|
+
# assets: [
|
70
|
+
# {
|
71
|
+
# "AssetID" => "9a24c8e2-1066-42fb-be1c-697c5ead476d",
|
72
|
+
# "AssetData" => "jakebellacera",
|
73
|
+
# "AssetType" => "TEXT",
|
74
|
+
# "Target" => "XX~username",
|
75
|
+
# "Client" => "XX",
|
76
|
+
# "Status" => "APPROVED",
|
77
|
+
# "AssetMeta" => "",
|
78
|
+
# "AssetRecordID" => "XX-91c8071a-1201-4f99-bc9d-f8d53a947dc1"
|
79
|
+
# },
|
80
|
+
# {
|
81
|
+
# "AssetID" => "9a24c8e2-1066-42fb-be1c-697c5ead476d",
|
82
|
+
# "AssetData" => "http://www.gravatar.com/avatar/98363013aa1237798130bc0fd2c4159d.png",
|
83
|
+
# "AssetType" => "IMAGE",
|
84
|
+
# "Target" => "XX~avatar",
|
85
|
+
# "Client" => "XX",
|
86
|
+
# "Status" => "APPROVED",
|
87
|
+
# "AssetMeta" => "",
|
88
|
+
# "AssetRecordID" => "XX-91c8071a-1201-4f99-bc9d-f8d53a947dc1"
|
74
89
|
# }
|
90
|
+
# ]
|
91
|
+
# }
|
75
92
|
def publish(assets)
|
76
|
-
puts "Available options: #{@options}" if @options[:debug]
|
77
|
-
@time = Time.now.strftime(@options[:time_format])
|
78
93
|
@record_id = generate_record_id
|
94
|
+
@time = Time.now.strftime(@options[:time_format])
|
79
95
|
|
96
|
+
# Build our XMLs
|
80
97
|
xmls = []
|
81
|
-
assets.each do |r|
|
82
|
-
xmls << build_xml(
|
98
|
+
validate(assets).each do |r|
|
99
|
+
xmls << build_xml(r[:target], r[:type].to_s.upcase, r[:value])
|
83
100
|
end
|
84
101
|
|
85
102
|
return {
|
@@ -88,41 +105,90 @@ module Pomade
|
|
88
105
|
}
|
89
106
|
end
|
90
107
|
|
91
|
-
|
92
|
-
#
|
93
|
-
# Parameters:
|
108
|
+
##
|
109
|
+
# Validates an array of assets.
|
94
110
|
#
|
95
|
-
#
|
111
|
+
# == Parameters
|
96
112
|
#
|
97
|
-
#
|
113
|
+
# * *assets* _(array)_ - A collection of assets. Each item consists of a hash with three keys: +:target+, +:type+ and +:value+. The values for keys +:target+ and +:value+ are both strings while the +:type+ key's value is a symbol. Available values are:
|
114
|
+
# * +:image+ -- An +IMAGE+ type asset
|
115
|
+
# * +:video+ -- A +VIDEO+ type asset
|
116
|
+
# * +:text+ -- A +TEXT+ type asset
|
117
|
+
#
|
118
|
+
# == Returns
|
119
|
+
#
|
120
|
+
# The +array+ of assets.
|
98
121
|
#
|
99
|
-
#
|
122
|
+
# == Example
|
123
|
+
#
|
124
|
+
# records = [
|
125
|
+
# { target: "XX~USERNAME", type: :text, value: "jakebellacera"},
|
126
|
+
# { target: "XX~AVATAR", type: :image, value: "http://www.gravatar.com/avatar/98363013aa1237798130bc0fd2c4159d.png"}
|
127
|
+
# ]
|
128
|
+
#
|
129
|
+
# @pom.validate(records)
|
130
|
+
# # =>
|
131
|
+
# [
|
132
|
+
# { target: "XX~USERNAME", type: :text, value: "jakebellacera"},
|
133
|
+
# { target: "XX~AVATAR", type: :image, value: "http://www.gravatar.com/avatar/98363013aa1237798130bc0fd2c4159d.png"}
|
134
|
+
# ]
|
135
|
+
def validate(assets)
|
136
|
+
available_keys = [:target, :type, :value].sort
|
137
|
+
|
138
|
+
assets.each do |a|
|
139
|
+
raise InvalidAssetKeys, "Each asset should only contain the keys: :target, :type, and :value." unless (a.keys & available_keys).sort == available_keys
|
140
|
+
raise InvalidAssetType, "Invalid asset type. Available choices are: :text, :image and :video." unless [:text, :image, :video].include?(a[:type])
|
141
|
+
test(a)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
##
|
148
|
+
# Generates a +SecureRandom.uuid+ (GUID) with the +client_id+ appended to it.
|
100
149
|
def generate_record_id
|
101
150
|
@client_id + '-' + SecureRandom.uuid
|
102
151
|
end
|
103
152
|
|
104
|
-
|
153
|
+
##
|
154
|
+
# Tests to see if an asset's +:value+ is correct in correlation to its +:type+.
|
155
|
+
def test(asset)
|
156
|
+
# If the value is a URL...
|
157
|
+
if (asset[:type] == :image || asset[:type] == :video) && url?(asset[:value])
|
158
|
+
raise BadAssetValueURL, "Please make sure your asset's value is a valid, working URL." unless Net::HTTP.get_response(URI(asset[:value])).code.to_i == 200
|
159
|
+
else
|
160
|
+
# Since the value was not a URL, we should raise an error for any IMAGE and VIDEO types.
|
161
|
+
raise BadImageValue, "assets with an :image type should have an image URL as the value." if asset[:type] == :image
|
162
|
+
raise BadVideoValue, "assets with a :video type should have a video URL as the value." if asset[:type] == :video
|
163
|
+
end
|
164
|
+
end
|
105
165
|
|
166
|
+
##
|
167
|
+
# Posts an XML to the Pomegranate instance and handles the response.
|
168
|
+
#
|
169
|
+
# *Note:* This method will fail if any requests are rejected.
|
170
|
+
#
|
171
|
+
# == Parameters
|
172
|
+
#
|
173
|
+
# * *body* _(string, XML)_ - A Pomegranate XML asset
|
174
|
+
#
|
175
|
+
# == Returns
|
176
|
+
#
|
177
|
+
# An +array+ of comiled Pomegranate assets
|
106
178
|
def post(data)
|
107
179
|
response_data = []
|
108
180
|
data.each do |xml|
|
109
|
-
puts xml if @options[:debug]
|
110
|
-
|
111
181
|
req = send_request(xml)
|
112
182
|
|
113
|
-
if req[:code]
|
114
|
-
puts "===> SUCCESS!" if @options[:debug]
|
183
|
+
if req[:code].to_i.between?(200, 201)
|
115
184
|
response_data << req[:data]
|
116
185
|
else
|
117
186
|
if req[:code] == "401"
|
118
|
-
|
119
|
-
puts "===> ERROR! Authentication" if @options[:debug]
|
187
|
+
raise ResponseError, "Could not authenticate with the Pomegranate API. Ensure that your credentials are correct."
|
120
188
|
elsif req[:code] == "400"
|
121
|
-
|
122
|
-
puts "===> ERROR! Formatting" if @options[:debug]
|
189
|
+
raise ResponseError, "Bad asset value formatting. Please reformat and try again."
|
123
190
|
else
|
124
|
-
|
125
|
-
puts "===> ERROR Unknown" if @options[:debug]
|
191
|
+
raise StandardError, "An unknown error has occured."
|
126
192
|
end
|
127
193
|
response_data = false
|
128
194
|
break
|
@@ -132,12 +198,17 @@ module Pomade
|
|
132
198
|
response_data
|
133
199
|
end
|
134
200
|
|
201
|
+
##
|
202
|
+
# Sends a request to Pomegranate
|
203
|
+
#
|
204
|
+
# == Parameters
|
205
|
+
#
|
206
|
+
# * *body* _(string, XML)_ - A Pomegranate XML asset
|
135
207
|
def send_request(body)
|
136
208
|
status = false
|
137
209
|
data = false
|
138
210
|
code = ""
|
139
211
|
|
140
|
-
puts "Initializing request for #{@subdomain + '.' + @options[:host]}" if @options[:debug]
|
141
212
|
Net::HTTP.start("#{@subdomain}.#{@options[:host]}", 80) do |http|
|
142
213
|
req = Net::HTTP::Post.new(@options[:pathname])
|
143
214
|
|
@@ -147,7 +218,6 @@ module Pomade
|
|
147
218
|
req.ntlm_auth(@username, @options[:domain], @password)
|
148
219
|
|
149
220
|
response = http.request(req)
|
150
|
-
puts response.inspect if @options[:debug]
|
151
221
|
|
152
222
|
code = response.code
|
153
223
|
|
@@ -161,7 +231,9 @@ module Pomade
|
|
161
231
|
{:code => code, :data => data}
|
162
232
|
end
|
163
233
|
|
164
|
-
|
234
|
+
##
|
235
|
+
# Builds a Pomegranate asset XML file
|
236
|
+
def build_xml(target, type, value)
|
165
237
|
<<-EOF.gsub(/^ {8}/, '')
|
166
238
|
<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
|
167
239
|
<entry
|
@@ -177,10 +249,10 @@ module Pomade
|
|
177
249
|
<content type="application/xml">
|
178
250
|
<m:properties>
|
179
251
|
<d:AssetID>--</d:AssetID>
|
180
|
-
<d:AssetData>#{value}</d:AssetData>
|
252
|
+
<d:AssetData>#{type === "TEXT" ? escape_xml(value) : value}</d:AssetData>
|
181
253
|
<d:AssetType>#{type}</d:AssetType>
|
182
254
|
<d:AssetMeta></d:AssetMeta>
|
183
|
-
<d:AssetRecordID>#{record_id}</d:AssetRecordID>
|
255
|
+
<d:AssetRecordID>#{@record_id}</d:AssetRecordID>
|
184
256
|
<d:Target>#{target}</d:Target>
|
185
257
|
<d:Client>#{@client_id}</d:Client>
|
186
258
|
<d:Status>APPROVED</d:Status>
|
@@ -190,6 +262,20 @@ module Pomade
|
|
190
262
|
EOF
|
191
263
|
end
|
192
264
|
|
265
|
+
##
|
266
|
+
# Escapes any illegal XML characters
|
267
|
+
def escape_xml(string)
|
268
|
+
string.gsub!("&", "&")
|
269
|
+
string.gsub!("<", "<")
|
270
|
+
string.gsub!(">", ">")
|
271
|
+
string.gsub!("'", "'")
|
272
|
+
string.gsub!("\"", """)
|
273
|
+
|
274
|
+
return string
|
275
|
+
end
|
276
|
+
|
277
|
+
##
|
278
|
+
# Parses XMLs and returns a hash
|
193
279
|
def parse_xml(xml)
|
194
280
|
parsed_xml = Nokogiri::XML(xml.gsub(/\n|\r| /, ""))
|
195
281
|
data = {}
|
@@ -198,5 +284,18 @@ module Pomade
|
|
198
284
|
end
|
199
285
|
data
|
200
286
|
end
|
287
|
+
|
288
|
+
##
|
289
|
+
# Tests if a string is a URL
|
290
|
+
def url?(string)
|
291
|
+
begin
|
292
|
+
uri = URI.parse(string)
|
293
|
+
%w( http https ).include?(uri.scheme)
|
294
|
+
rescue URI::BadURIError
|
295
|
+
false
|
296
|
+
rescue URI::InvalidURIError
|
297
|
+
false
|
298
|
+
end
|
299
|
+
end
|
201
300
|
end
|
202
301
|
end
|
data/lib/pomade/version.rb
CHANGED
data/readme.md
CHANGED
@@ -33,51 +33,64 @@ These are the available options and their defaults.
|
|
33
33
|
host: 'timessquare2.com', # [string] The host (domain name) that Pomegranate lives on.
|
34
34
|
pathname: '/p/p.svc/Assets/', # [string] The path that is used for interacting with Pomegranate.
|
35
35
|
time_format: "%Y-%m-%dT%H:%M:%SZ", # [string] (strftime) change the layout of the timestamp.
|
36
|
-
login_domain: nil
|
37
|
-
debug: false # [boolean] Turns on debug mode. This will print out any activity.
|
36
|
+
login_domain: nil # [string] NTLM login domain.
|
38
37
|
}
|
39
38
|
```
|
40
39
|
|
41
40
|
### Usage
|
42
41
|
|
43
|
-
To publish assets to Pomegranate, simply create a new Publisher instance.
|
42
|
+
To publish assets to Pomegranate, simply create a new `Pomade::Publisher` instance.
|
44
43
|
|
45
44
|
```ruby
|
46
45
|
@pom = Pomade::Publisher.new('my-subdomain', 'myusername', 'mypassword', 'XX')
|
47
46
|
```
|
48
47
|
|
49
|
-
Next, you'll want to push your assets to Pomegranate. You can do this by building an array of hashes. Each item in the array represents a single asset and they each have three keys:
|
48
|
+
Next, you'll want to push your assets to Pomegranate. You can do this by building an array of hashes. Each item in the array represents a single asset and they each have three keys: **:target**, **:type** and **:value**. You'll pass this array into the `publish` method.
|
50
49
|
|
51
50
|
```ruby
|
52
51
|
assets = [
|
53
|
-
|
54
|
-
|
52
|
+
{ target: "XX~username", type: :text, value: "jakebellacera"},
|
53
|
+
{ target: "XX~avatar", type: :image, value: "http://www.gravatar.com/avatar/98363013aa1237798130bc0fd2c4159d.png"}
|
55
54
|
]
|
56
55
|
|
57
56
|
record = @pom.publish(assets)
|
58
57
|
```
|
59
58
|
|
60
|
-
The `
|
59
|
+
The `publish` method will return a **record**. A record is a hash with two keys: **:record_id** and **:assets**. The `:record_id` is a randomly generated UUID string with your client_id prepended to it while the `:assets` array is the posted assets.
|
61
60
|
|
62
61
|
```ruby
|
63
62
|
puts record
|
64
|
-
#=>
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
#=>
|
64
|
+
{
|
65
|
+
record_id: "XX-91c8071a-1201-4f99-bc9d-f8d53a947dc1",
|
66
|
+
assets: [
|
67
|
+
{
|
68
|
+
"AssetID" => "9a24c8e2-1066-42fb-be1c-697c5ead476d",
|
69
|
+
"AssetData" => "jakebellacera",
|
70
|
+
"AssetType" => "TEXT",
|
71
|
+
"Target" => "XX~username",
|
72
|
+
"Client" => "XX",
|
73
|
+
"Status" => "APPROVED",
|
74
|
+
"AssetMeta" => "",
|
75
|
+
"AssetRecordID" => "XX-91c8071a-1201-4f99-bc9d-f8d53a947dc1"
|
76
|
+
},
|
77
|
+
{
|
78
|
+
"AssetID" => "9a24c8e2-1066-42fb-be1c-697c5ead476d",
|
79
|
+
"AssetData" => "http://www.gravatar.com/avatar/98363013aa1237798130bc0fd2c4159d.png",
|
80
|
+
"AssetType" => "IMAGE",
|
81
|
+
"Target" => "XX~avatar",
|
82
|
+
"Client" => "XX",
|
83
|
+
"Status" => "APPROVED",
|
84
|
+
"AssetMeta" => "",
|
85
|
+
"AssetRecordID" => "XX-91c8071a-1201-4f99-bc9d-f8d53a947dc1"
|
70
86
|
}
|
87
|
+
]
|
88
|
+
}
|
71
89
|
```
|
72
90
|
|
73
|
-
####
|
74
|
-
|
75
|
-
Sometimes Pomegranate will not be able to accept your request. If you're getting a 400 error, it's most likely a formatting issue. Since the errors returned by Pomegranate are not very verbose, it's best to run through a simple checklist instead:
|
91
|
+
#### Validation
|
76
92
|
|
77
|
-
|
78
|
-
* Make sure that the targets and types for each asset are correct. Targets will vary from client to client.
|
79
|
-
* Try using the `debug` option.
|
80
|
-
* If all else fails, you can try submitting an [issue](https://github.com/jakebellacera/pomade/issues). Please be specific in your bug report.
|
93
|
+
Once you attempt to publish your assets, `Publisher` will attempt to validate your assets. Most of the time it will work, as Publisher will check your URLS for :image and :video types and ensure that they resolve properly. This validation may not find everything and you'll still get a bad response from Pomegranate, if that's the case, please [file a bug](http://github.com/jakebellacera/pomade/issues) with the steps you took to reproduce the problem.
|
81
94
|
|
82
95
|
## Contributing
|
83
96
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pomade
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
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: 2012-09-
|
12
|
+
date: 2012-09-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ruby-ntlm
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- LICENSE.txt
|
56
56
|
- Rakefile
|
57
57
|
- lib/pomade.rb
|
58
|
+
- lib/pomade/exceptions.rb
|
58
59
|
- lib/pomade/publisher.rb
|
59
60
|
- lib/pomade/version.rb
|
60
61
|
- pomade.gemspec
|