helix 0.0.4.3.pre → 0.0.4.4.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/helix/base.rb +3 -2
- data/lib/helix/builds_urls.rb +67 -0
- data/lib/helix/config.rb +9 -206
- data/lib/helix/has_signatures.rb +84 -0
- data/lib/helix/library.rb +11 -0
- data/lib/helix/paginates.rb +91 -0
- data/spec/base_spec.rb +5 -0
- data/spec/config_spec.rb +0 -28
- data/spec/library_spec.rb +1 -1
- data/spec/paginates_spec.rb +51 -0
- metadata +6 -2
data/lib/helix/base.rb
CHANGED
@@ -137,8 +137,8 @@ module Helix
|
|
137
137
|
base_url_opts = {content_type: (opts[:content_type] || :json)}
|
138
138
|
url = memo_cfg.build_url(base_url_opts.merge(guid: self.guid, resource_label: plural_resource_label))
|
139
139
|
# We allow opts[:sig_type] for internal negative testing only.
|
140
|
-
raw_attrs
|
141
|
-
@attributes
|
140
|
+
raw_attrs = memo_cfg.get_response(url, {sig_type: :view}.merge(opts))
|
141
|
+
@attributes = massage_raw_attrs(raw_attrs)
|
142
142
|
self
|
143
143
|
end
|
144
144
|
alias_method :reload, :load
|
@@ -166,6 +166,7 @@ module Helix
|
|
166
166
|
private
|
167
167
|
|
168
168
|
def massage_raw_attrs(raw_attrs)
|
169
|
+
return raw_attrs['site'] if raw_attrs.respond_to?(:has_key?) && raw_attrs.has_key?('site')
|
169
170
|
# FIXME: Albums JSON output is embedded as the only member of an Array.
|
170
171
|
proper_hash = raw_attrs.respond_to?(:has_key?) && raw_attrs.has_key?(guid_name)
|
171
172
|
proper_hash ? raw_attrs : raw_attrs.first
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Helix
|
2
|
+
|
3
|
+
module BuildsUrls
|
4
|
+
|
5
|
+
unless defined?(self::SCOPES)
|
6
|
+
SCOPES = [:reseller, :company, :library]
|
7
|
+
end
|
8
|
+
|
9
|
+
# Creates additional URL stubbing that can be used in conjuction
|
10
|
+
# with the base_url to create RESTful URLs
|
11
|
+
#
|
12
|
+
# @param [String] base_url the base part of the URL to be used
|
13
|
+
# @param [Hash] opts a hash of options for building URL additions
|
14
|
+
# @return [String] The full RESTful URL string object
|
15
|
+
def add_sub_urls(base_url, opts)
|
16
|
+
guid, action, format = [:guid, :action, :formats].map { |sub| opts[sub] }
|
17
|
+
url = sub_url_scoping(base_url, opts)
|
18
|
+
url += "/#{guid}" if guid
|
19
|
+
url += "/formats/#{format}" if format
|
20
|
+
url += "/#{action}" if action
|
21
|
+
return url if opts[:content_type].blank?
|
22
|
+
"#{url}.#{opts[:content_type]}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Creates a full RESTful URL to be used for HTTP requests.
|
26
|
+
#
|
27
|
+
# @param [Hash] opts a hash of options for building URL
|
28
|
+
# @return [String] The full RESTful URL string object
|
29
|
+
def build_url(opts={})
|
30
|
+
opts[:content_type] ||= :xml
|
31
|
+
opts[:resource_label] ||= :videos
|
32
|
+
base_url = get_base_url(opts)
|
33
|
+
url = add_sub_urls(base_url, opts)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Creates the base url with information collected from credentials.
|
37
|
+
#
|
38
|
+
# @param [Hash] opts a hash of options for building URL
|
39
|
+
# @return [String] The base RESTful URL string object
|
40
|
+
def get_base_url(opts)
|
41
|
+
creds = credentials
|
42
|
+
base_url = creds[:site]
|
43
|
+
return base_url if opts[:guid] || opts[:action] == :create_many
|
44
|
+
reseller, company, library = SCOPES.map { |scope| creds[scope] }
|
45
|
+
base_url += "/resellers/#{reseller}" if reseller
|
46
|
+
if company
|
47
|
+
base_url += "/companies/#{company}"
|
48
|
+
base_url += "/libraries/#{library}" if library
|
49
|
+
end
|
50
|
+
base_url
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def sub_url_scoping(base_url, opts)
|
56
|
+
resource_label = opts[:resource_label]
|
57
|
+
if resource_label == 'libraries' and base_url !~ /companies/
|
58
|
+
co_id = opts[:company] || credentials[:company]
|
59
|
+
raise "No company to scope to: #{credentials}" if co_id.nil?
|
60
|
+
resource_label = "companies/#{co_id}/libraries"
|
61
|
+
end
|
62
|
+
"#{base_url}/#{resource_label}"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/helix/config.rb
CHANGED
@@ -2,26 +2,27 @@ require 'helix/video'
|
|
2
2
|
require 'helix/track'
|
3
3
|
require 'helix/album'
|
4
4
|
require 'helix/image'
|
5
|
+
require 'helix/builds_urls'
|
6
|
+
require 'helix/has_signatures'
|
7
|
+
require 'helix/paginates'
|
5
8
|
require 'singleton'
|
6
9
|
|
7
10
|
module Helix
|
8
11
|
|
9
12
|
class Config
|
10
13
|
|
14
|
+
include BuildsUrls
|
15
|
+
include HasSignatures
|
16
|
+
include Paginates
|
11
17
|
include Singleton
|
12
18
|
|
13
19
|
unless defined?(self::DEFAULT_FILENAME)
|
14
20
|
DEFAULT_FILENAME = './helix.yml'
|
15
|
-
ITEMS_PER_PAGE = 100
|
16
|
-
SCOPES = [:reseller, :company, :library]
|
17
|
-
SIG_DURATION = 1200 # in minutes
|
18
|
-
STARTING_PAGE = 1
|
19
|
-
TIME_OFFSET = 1000 * 60 # 1000 minutes, lower to give some margin of error
|
20
|
-
VALID_SIG_TYPES = [ :ingest, :update, :view ]
|
21
21
|
end
|
22
22
|
|
23
|
-
attr_accessor :credentials
|
24
|
-
attr_reader :response
|
23
|
+
attr_accessor :credentials # local
|
24
|
+
attr_reader :response # in Paginates
|
25
|
+
attr_reader :signature_for, :signature_expiration_for # in HasSignatures
|
25
26
|
|
26
27
|
# Creates a singleton of itself, setting the config
|
27
28
|
# to a specified YAML file. If no file is specified the default
|
@@ -42,122 +43,6 @@ module Helix
|
|
42
43
|
config
|
43
44
|
end
|
44
45
|
|
45
|
-
# Creates additional URL stubbing that can be used in conjuction
|
46
|
-
# with the base_url to create RESTful URLs
|
47
|
-
#
|
48
|
-
# @param [String] base_url the base part of the URL to be used
|
49
|
-
# @param [Hash] opts a hash of options for building URL additions
|
50
|
-
# @return [String] The full RESTful URL string object
|
51
|
-
def add_sub_urls(base_url, opts)
|
52
|
-
guid, action, format = [:guid, :action, :formats].map { |sub| opts[sub] }
|
53
|
-
url = sub_url_scoping(base_url, opts)
|
54
|
-
url += "/#{guid}" if guid
|
55
|
-
url += "/formats/#{format}" if format
|
56
|
-
url += "/#{action}" if action
|
57
|
-
return url if opts[:content_type].blank?
|
58
|
-
"#{url}.#{opts[:content_type]}"
|
59
|
-
end
|
60
|
-
|
61
|
-
# Creates a full RESTful URL to be used for HTTP requests.
|
62
|
-
#
|
63
|
-
# @param [Hash] opts a hash of options for building URL
|
64
|
-
# @return [String] The full RESTful URL string object
|
65
|
-
def build_url(opts={})
|
66
|
-
opts[:content_type] ||= :xml
|
67
|
-
opts[:resource_label] ||= :videos
|
68
|
-
base_url = get_base_url(opts)
|
69
|
-
url = add_sub_urls(base_url, opts)
|
70
|
-
end
|
71
|
-
|
72
|
-
def clear_signatures!
|
73
|
-
@signature_for = {}
|
74
|
-
@signature_expiration_for = {}
|
75
|
-
end
|
76
|
-
|
77
|
-
# Makes aggregated calls to get_response with pagination
|
78
|
-
# folding/injecting/accumulating the results into a single output set.
|
79
|
-
#
|
80
|
-
# @param [String] url the base part of the URL to be used
|
81
|
-
# @param [String] plural_resource_label: "videos", "tracks", etc.
|
82
|
-
# @param [Hash] original_opts a hash of options for building URL additions
|
83
|
-
# @return [Array] The accumulated attribute Hashes for ORM instances
|
84
|
-
def get_aggregated_data_sets(url, plural_resource_label, original_opts={})
|
85
|
-
data_sets, page, per_page = [], STARTING_PAGE
|
86
|
-
begin
|
87
|
-
aggregation_opts = {page: page, per_page: ITEMS_PER_PAGE}.merge(original_opts)
|
88
|
-
raw_response = get_response(url, {sig_type: :view}.merge(aggregation_opts))
|
89
|
-
data_set = raw_response[plural_resource_label]
|
90
|
-
data_sets += data_set if data_set
|
91
|
-
page += 1
|
92
|
-
end until last_page?
|
93
|
-
data_sets
|
94
|
-
end
|
95
|
-
|
96
|
-
# Creates the base url with information collected from credentials.
|
97
|
-
#
|
98
|
-
# @param [Hash] opts a hash of options for building URL
|
99
|
-
# @return [String] The base RESTful URL string object
|
100
|
-
def get_base_url(opts)
|
101
|
-
creds = credentials
|
102
|
-
base_url = creds[:site]
|
103
|
-
return base_url if opts[:guid] || opts[:action] == :create_many
|
104
|
-
reseller, company, library = SCOPES.map { |scope| creds[scope] }
|
105
|
-
base_url += "/resellers/#{reseller}" if reseller
|
106
|
-
if company
|
107
|
-
base_url += "/companies/#{company}"
|
108
|
-
base_url += "/libraries/#{library}" if library
|
109
|
-
end
|
110
|
-
base_url
|
111
|
-
end
|
112
|
-
|
113
|
-
# Creates additional URL stubbing that can be used in conjuction
|
114
|
-
# with the base_url to create RESTful URLs
|
115
|
-
#
|
116
|
-
# @param [String] url the base part of the URL to be used
|
117
|
-
# @param [Hash] original_opts a hash of options for building URL additions
|
118
|
-
# @return [String] The full RESTful URL string object
|
119
|
-
def get_response(url, original_opts={})
|
120
|
-
opts = massage_custom_fields_in(original_opts)
|
121
|
-
sig_type = opts.delete(:sig_type)
|
122
|
-
params = opts.merge(signature: signature(sig_type, opts))
|
123
|
-
begin
|
124
|
-
@response = RestClient.get(url, params: params)
|
125
|
-
rescue RestClient::InternalServerError => e
|
126
|
-
raise NetworkError, "Unable to access url #{url} with params #{params}"
|
127
|
-
end
|
128
|
-
parse_response_by_url_format(@response, url)
|
129
|
-
end
|
130
|
-
|
131
|
-
# Reports whether the most recent response's headers have a true :is_last_page value
|
132
|
-
#
|
133
|
-
# @return [Boolean] As above. Returns false if no such header is found,
|
134
|
-
# or if there is an explictly false value.
|
135
|
-
def last_page?
|
136
|
-
return false unless @response
|
137
|
-
return false unless @response.headers
|
138
|
-
return true unless @response.headers.has_key?(:is_last_page)
|
139
|
-
@response.headers[:is_last_page] == "true"
|
140
|
-
end
|
141
|
-
|
142
|
-
# Fetches the signature for a specific license key.
|
143
|
-
#
|
144
|
-
# @param [Symbol] sig_type The type of signature required for calls.
|
145
|
-
# @param [Hash] opts allows you to overide contributor and license_id
|
146
|
-
# @return [String] The signature needed to pass around for calls.
|
147
|
-
def signature(sig_type, opts={})
|
148
|
-
prepare_signature_memoization
|
149
|
-
memo_sig = existing_sig_for(sig_type)
|
150
|
-
return memo_sig if memo_sig
|
151
|
-
unless VALID_SIG_TYPES.include?(sig_type)
|
152
|
-
raise ArgumentError, error_message_for(sig_type)
|
153
|
-
end
|
154
|
-
|
155
|
-
lk = license_key
|
156
|
-
@signature_expiration_for[lk][sig_type] = Time.now + TIME_OFFSET
|
157
|
-
new_sig_url = url_for(sig_type, opts)
|
158
|
-
@signature_for[lk][sig_type] = RestClient.get(new_sig_url)
|
159
|
-
end
|
160
|
-
|
161
46
|
def proxy
|
162
47
|
if @credentials[:proxy_uri]
|
163
48
|
protocol, uri = @credentials[:proxy_uri].split "://"
|
@@ -172,88 +57,6 @@ module Helix
|
|
172
57
|
end
|
173
58
|
end
|
174
59
|
|
175
|
-
private
|
176
|
-
|
177
|
-
def sub_url_scoping(base_url, opts)
|
178
|
-
resource_label = opts[:resource_label]
|
179
|
-
if resource_label == 'libraries' and base_url !~ /companies/
|
180
|
-
co_id = opts[:company] || credentials[:company]
|
181
|
-
raise "No company to scope to: #{credentials}" if co_id.nil?
|
182
|
-
resource_label = "companies/#{co_id}/libraries"
|
183
|
-
end
|
184
|
-
"#{base_url}/#{resource_label}"
|
185
|
-
end
|
186
|
-
|
187
|
-
def error_message_for(sig_type)
|
188
|
-
"I don't understand '#{sig_type}'. Please give me one of :ingest, :update, or :view."
|
189
|
-
end
|
190
|
-
|
191
|
-
def existing_sig_for(sig_type)
|
192
|
-
return if sig_expired_for?(sig_type)
|
193
|
-
@signature_for[license_key][sig_type]
|
194
|
-
end
|
195
|
-
|
196
|
-
def get_contributor_library_company(opts)
|
197
|
-
sig_param_labels = [:contributor, :library, :company]
|
198
|
-
scoping_proc = lambda { |key| opts[key] || credentials[key] }
|
199
|
-
contributor, library, company = sig_param_labels.map(&scoping_proc)
|
200
|
-
contributor ||= 'helix_default_contributor'
|
201
|
-
[contributor, library, company]
|
202
|
-
end
|
203
|
-
|
204
|
-
def license_key
|
205
|
-
@credentials[:license_key]
|
206
|
-
end
|
207
|
-
|
208
|
-
def massage_custom_fields_in(opts)
|
209
|
-
return opts.clone unless opts.has_key?(:custom_fields)
|
210
|
-
cf_opts = opts.delete(:custom_fields)
|
211
|
-
cf_opts.inject(opts.clone) do |memo,pair|
|
212
|
-
k,v = pair
|
213
|
-
memo.merge("custom_fields[#{k}]" => v)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
def parse_response_by_url_format(response, url)
|
218
|
-
### FIXME: This is ugly. Clean it up.
|
219
|
-
if url =~ /json/
|
220
|
-
JSON.parse(response)
|
221
|
-
elsif url =~ /xml/
|
222
|
-
#TODO: Cleanup Nori and response gsub.
|
223
|
-
Nori.parser = :nokogiri
|
224
|
-
xml = response.gsub(/<custom-fields type='array'>/, '<custom-fields type=\'hash\'>')
|
225
|
-
Nori.parse(xml)
|
226
|
-
elsif url =~ /csv/
|
227
|
-
response
|
228
|
-
else
|
229
|
-
raise "Could not parse #{url}"
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
def prepare_signature_memoization
|
234
|
-
lk = license_key
|
235
|
-
@signature_for ||= {}
|
236
|
-
@signature_expiration_for ||= {}
|
237
|
-
@signature_for[lk] ||= {}
|
238
|
-
@signature_expiration_for[lk] ||= {}
|
239
|
-
end
|
240
|
-
|
241
|
-
def sig_expired_for?(sig_type)
|
242
|
-
expires_at = @signature_expiration_for[license_key][sig_type]
|
243
|
-
return true if expires_at.nil?
|
244
|
-
expires_at <= Time.now
|
245
|
-
end
|
246
|
-
|
247
|
-
def url_for(sig_type, opts={})
|
248
|
-
contributor, library, company = get_contributor_library_company(opts)
|
249
|
-
url = "#{credentials[:site]}/api/#{sig_type}_key?"
|
250
|
-
url += "licenseKey=#{credentials[:license_key]}&duration=#{SIG_DURATION}"
|
251
|
-
url += "&contributor=#{contributor}" if sig_type == :ingest
|
252
|
-
url += "&library_id=#{library}" if library
|
253
|
-
url += "&company_id=#{company}" if company
|
254
|
-
url
|
255
|
-
end
|
256
|
-
|
257
60
|
end
|
258
61
|
|
259
62
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Helix
|
2
|
+
|
3
|
+
module HasSignatures
|
4
|
+
|
5
|
+
unless defined?(self::VALID_SIG_TYPES)
|
6
|
+
SIG_DURATION = 1200 # in minutes
|
7
|
+
TIME_OFFSET = 1000 * 60 # 1000 minutes, lower to give some margin of error
|
8
|
+
VALID_SIG_TYPES = [ :ingest, :update, :view ]
|
9
|
+
end
|
10
|
+
|
11
|
+
def clear_signatures!
|
12
|
+
@signature_for = {}
|
13
|
+
@signature_expiration_for = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Fetches the signature for a specific license key.
|
17
|
+
#
|
18
|
+
# @param [Symbol] sig_type The type of signature required for calls.
|
19
|
+
# @param [Hash] opts allows you to overide contributor and license_id
|
20
|
+
# @return [String] The signature needed to pass around for calls.
|
21
|
+
def signature(sig_type, opts={})
|
22
|
+
prepare_signature_memoization
|
23
|
+
memo_sig = existing_sig_for(sig_type)
|
24
|
+
return memo_sig if memo_sig
|
25
|
+
unless VALID_SIG_TYPES.include?(sig_type)
|
26
|
+
raise ArgumentError, error_message_for(sig_type)
|
27
|
+
end
|
28
|
+
|
29
|
+
lk = license_key
|
30
|
+
@signature_expiration_for[lk][sig_type] = Time.now + TIME_OFFSET
|
31
|
+
new_sig_url = signature_url_for(sig_type, opts)
|
32
|
+
@signature_for[lk][sig_type] = RestClient.get(new_sig_url)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def error_message_for(sig_type)
|
38
|
+
"I don't understand '#{sig_type}'. Please give me one of :ingest, :update, or :view."
|
39
|
+
end
|
40
|
+
|
41
|
+
def existing_sig_for(sig_type)
|
42
|
+
return if sig_expired_for?(sig_type)
|
43
|
+
@signature_for[license_key][sig_type]
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_contributor_library_company(opts)
|
47
|
+
sig_param_labels = [:contributor, :library, :company]
|
48
|
+
scoping_proc = lambda { |key| opts[key] || credentials[key] }
|
49
|
+
contributor, library, company = sig_param_labels.map(&scoping_proc)
|
50
|
+
contributor ||= 'helix_default_contributor'
|
51
|
+
[contributor, library, company]
|
52
|
+
end
|
53
|
+
|
54
|
+
def license_key
|
55
|
+
@credentials[:license_key]
|
56
|
+
end
|
57
|
+
|
58
|
+
def prepare_signature_memoization
|
59
|
+
lk = license_key
|
60
|
+
@signature_for ||= {}
|
61
|
+
@signature_expiration_for ||= {}
|
62
|
+
@signature_for[lk] ||= {}
|
63
|
+
@signature_expiration_for[lk] ||= {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def sig_expired_for?(sig_type)
|
67
|
+
expires_at = @signature_expiration_for[license_key][sig_type]
|
68
|
+
return true if expires_at.nil?
|
69
|
+
expires_at <= Time.now
|
70
|
+
end
|
71
|
+
|
72
|
+
def signature_url_for(sig_type, opts={})
|
73
|
+
contributor, library, company = get_contributor_library_company(opts)
|
74
|
+
url = "#{credentials[:site]}/api/#{sig_type}_key?"
|
75
|
+
url += "licenseKey=#{credentials[:license_key]}&duration=#{SIG_DURATION}"
|
76
|
+
url += "&contributor=#{contributor}" if sig_type == :ingest
|
77
|
+
url += "&library_id=#{library}" if library
|
78
|
+
url += "&company_id=#{company}" if company
|
79
|
+
url
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/lib/helix/library.rb
CHANGED
@@ -13,6 +13,16 @@ module Helix
|
|
13
13
|
super(nickname, opts.merge(content_type: :xml))
|
14
14
|
end
|
15
15
|
|
16
|
+
# Creates a string that associates to the class id.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# Helix::Library.guid_name #=> "name"
|
20
|
+
#
|
21
|
+
# @return [String] The guid name for a specific class.
|
22
|
+
def self.guid_name
|
23
|
+
"name"
|
24
|
+
end
|
25
|
+
|
16
26
|
# The class name, to be used by supporting classes. Such as Config which uses
|
17
27
|
# this method as a way to build URLs.
|
18
28
|
#
|
@@ -33,6 +43,7 @@ module Helix
|
|
33
43
|
"libraries"
|
34
44
|
end
|
35
45
|
|
46
|
+
#TODO: Revisit and document.
|
36
47
|
def self.known_attributes
|
37
48
|
[:player_profile, :ingest_profile, :secure_stream_callback_url, :hooks_attributes]
|
38
49
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Helix
|
2
|
+
|
3
|
+
module Paginates
|
4
|
+
|
5
|
+
unless defined?(self::STARTING_PAGE)
|
6
|
+
ITEMS_PER_PAGE = 100
|
7
|
+
STARTING_PAGE = 1
|
8
|
+
end
|
9
|
+
|
10
|
+
# Makes aggregated calls to get_response with pagination
|
11
|
+
# folding/injecting/accumulating the results into a single output set.
|
12
|
+
#
|
13
|
+
# @param [String] url the base part of the URL to be used
|
14
|
+
# @param [String] plural_resource_label: "videos", "tracks", etc.
|
15
|
+
# @param [Hash] original_opts a hash of options for building URL additions
|
16
|
+
# @return [Array] The accumulated attribute Hashes for ORM instances
|
17
|
+
def get_aggregated_data_sets(url, plural_resource_label, original_opts={})
|
18
|
+
data_sets, page, per_page = [], STARTING_PAGE
|
19
|
+
begin
|
20
|
+
aggregation_opts = {page: page, per_page: ITEMS_PER_PAGE}.merge(original_opts)
|
21
|
+
raw_response = get_response(url, {sig_type: :view}.merge(aggregation_opts))
|
22
|
+
data_set = raw_response[plural_resource_label]
|
23
|
+
data_sets += data_set if data_set
|
24
|
+
page += 1
|
25
|
+
end until last_page? || specific_page_requested?(original_opts)
|
26
|
+
data_sets
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates additional URL stubbing that can be used in conjuction
|
30
|
+
# with the base_url to create RESTful URLs
|
31
|
+
#
|
32
|
+
# @param [String] url the base part of the URL to be used
|
33
|
+
# @param [Hash] original_opts a hash of options for building URL additions
|
34
|
+
# @return [String] The full RESTful URL string object
|
35
|
+
def get_response(url, original_opts={})
|
36
|
+
opts = massage_custom_fields_in(original_opts)
|
37
|
+
sig_type = opts.delete(:sig_type)
|
38
|
+
params = opts.merge(signature: signature(sig_type, opts))
|
39
|
+
begin
|
40
|
+
@response = RestClient.get(url, params: params)
|
41
|
+
rescue RestClient::InternalServerError => e
|
42
|
+
raise NetworkError, "Unable to access url #{url} with params #{params}"
|
43
|
+
end
|
44
|
+
parse_response_by_url_format(@response, url)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Reports whether the most recent response's headers have a true :is_last_page value
|
50
|
+
#
|
51
|
+
# @return [Boolean] As above. Returns false if no such header is found,
|
52
|
+
# or if there is an explictly false value.
|
53
|
+
def last_page?
|
54
|
+
return false unless @response
|
55
|
+
return false unless @response.headers
|
56
|
+
return true unless @response.headers.has_key?(:is_last_page)
|
57
|
+
@response.headers[:is_last_page] == "true"
|
58
|
+
end
|
59
|
+
|
60
|
+
def massage_custom_fields_in(opts)
|
61
|
+
return opts.clone unless opts.has_key?(:custom_fields)
|
62
|
+
cf_opts = opts.delete(:custom_fields)
|
63
|
+
cf_opts.inject(opts.clone) do |memo,pair|
|
64
|
+
k,v = pair
|
65
|
+
memo.merge("custom_fields[#{k}]" => v)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def specific_page_requested?(original_opts)
|
70
|
+
original_opts.has_key?(:page)
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_response_by_url_format(response, url)
|
74
|
+
### FIXME: This is ugly. Clean it up.
|
75
|
+
if url =~ /json/
|
76
|
+
JSON.parse(response)
|
77
|
+
elsif url =~ /xml/
|
78
|
+
#TODO: Cleanup Nori and response gsub.
|
79
|
+
Nori.parser = :nokogiri
|
80
|
+
xml = response.gsub(/<custom-fields type='array'>/, '<custom-fields type=\'hash\'>')
|
81
|
+
Nori.parse(xml)
|
82
|
+
elsif url =~ /csv/
|
83
|
+
response
|
84
|
+
else
|
85
|
+
raise "Could not parse #{url}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/spec/base_spec.rb
CHANGED
@@ -289,6 +289,11 @@ describe Helix::Base do
|
|
289
289
|
subject { obj.send(meth, raw_attrs) }
|
290
290
|
it { should eq(nil) }
|
291
291
|
end
|
292
|
+
context "when given {'site' => :site_contents}" do
|
293
|
+
let(:raw_attrs) { {'site' => :site_contents} }
|
294
|
+
subject { obj.send(meth, raw_attrs) }
|
295
|
+
it { should eq(:site_contents) }
|
296
|
+
end
|
292
297
|
context "when given { guid_name => :the_val }" do
|
293
298
|
let(:raw_attrs) { { guid_name => :the_val } }
|
294
299
|
subject { obj.send(meth, raw_attrs) }
|
data/spec/config_spec.rb
CHANGED
@@ -336,34 +336,6 @@ describe Helix::Config do
|
|
336
336
|
end
|
337
337
|
end
|
338
338
|
|
339
|
-
describe "#get_aggregated_data_sets" do
|
340
|
-
let(:meth) { :get_aggregated_data_sets }
|
341
|
-
subject { obj.method(meth) }
|
342
|
-
its(:arity) { should eq(-3) }
|
343
|
-
context "when called" do
|
344
|
-
let(:opts) { {opts_key1: :opts_val1, per_page: 99} }
|
345
|
-
let(:label) { :videos }
|
346
|
-
before(:each) do
|
347
|
-
obj.stub(:signature) { :the_sig }
|
348
|
-
end
|
349
|
-
subject { obj.send(meth, :a_url, label, opts) }
|
350
|
-
it "should successively call RestClient.get with the opts arg merged with pagination info and return the parsed results" do
|
351
|
-
base_opts = {opts_key1: :opts_val1, per_page: 99, signature: :the_sig}
|
352
|
-
opts1 = {params: base_opts.merge(page: 1)}
|
353
|
-
opts2 = {params: base_opts.merge(page: 2)}
|
354
|
-
opts3 = {params: base_opts.merge(page: 3)}
|
355
|
-
non_final_response = double(String, headers: {is_last_page: 'false'})
|
356
|
-
final_response = double(String, headers: {is_last_page: 'true'})
|
357
|
-
RestClient.should_receive(:get).with(:a_url, opts1) { non_final_response }
|
358
|
-
RestClient.should_receive(:get).with(:a_url, opts2) { non_final_response }
|
359
|
-
RestClient.should_receive(:get).with(:a_url, opts3) { final_response }
|
360
|
-
obj.stub(:parse_response_by_url_format).with(non_final_response, :a_url) { {label => [:non_final]} }
|
361
|
-
obj.stub(:parse_response_by_url_format).with(final_response, :a_url) { {label => [:final]} }
|
362
|
-
expect(obj.send(meth, :a_url, label, opts)).to eq([:non_final, :non_final, :final])
|
363
|
-
end
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
339
|
describe "#get_response" do
|
368
340
|
let(:meth) { :get_response }
|
369
341
|
subject { obj.method(meth) }
|
data/spec/library_spec.rb
CHANGED
@@ -10,7 +10,7 @@ describe Helix::Library do
|
|
10
10
|
mods.each { |mod| its(:ancestors) { should include(mod) } }
|
11
11
|
its(:ancestors) { should_not include(Helix::Media) }
|
12
12
|
|
13
|
-
its(:guid_name) { should eq('
|
13
|
+
its(:guid_name) { should eq('name') }
|
14
14
|
its(:resource_label_sym) { should be(:library) }
|
15
15
|
its(:plural_resource_label) { should eq('libraries') }
|
16
16
|
[:find, :create, :all, :find_all, :where].each do |crud_call|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'helix'
|
3
|
+
|
4
|
+
describe Helix::Paginates do
|
5
|
+
|
6
|
+
class DummyClass;end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@klass = DummyClass.new
|
10
|
+
@klass.extend(Helix::Paginates)
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
describe "#get_aggregated_data_sets" do
|
15
|
+
let(:meth) { :get_aggregated_data_sets }
|
16
|
+
subject { @klass.method(meth) }
|
17
|
+
its(:arity) { should eq(-3) }
|
18
|
+
context "when called" do
|
19
|
+
let(:opts) { {opts_key1: :opts_val1, per_page: 99} }
|
20
|
+
let(:label) { :videos }
|
21
|
+
let(:base_opts) { {opts_key1: :opts_val1, per_page: 99, signature: :the_sig} }
|
22
|
+
let(:opts1) { {params: base_opts.merge(page: 1)} }
|
23
|
+
let(:opts2) { {params: base_opts.merge(page: 2)} }
|
24
|
+
let(:opts3) { {params: base_opts.merge(page: 3)} }
|
25
|
+
before(:each) { @klass.stub(:signature) { :the_sig } }
|
26
|
+
subject { @klass.send(meth, :a_url, label, opts) }
|
27
|
+
it "should successively call RestClient.get with the opts arg merged with pagination info and return the parsed results" do
|
28
|
+
non_final_response = double(String, headers: {is_last_page: 'false'})
|
29
|
+
final_response = double(String, headers: {is_last_page: 'true'})
|
30
|
+
RestClient.should_receive(:get).with(:a_url, opts1) { non_final_response }
|
31
|
+
RestClient.should_receive(:get).with(:a_url, opts2) { non_final_response }
|
32
|
+
RestClient.should_receive(:get).with(:a_url, opts3) { final_response }
|
33
|
+
@klass.stub(:parse_response_by_url_format).with(non_final_response, :a_url) { {label => [:non_final]} }
|
34
|
+
@klass.stub(:parse_response_by_url_format).with(final_response, :a_url) { {label => [:final]} }
|
35
|
+
expect(@klass.send(meth, :a_url, label, opts)).to eq([:non_final, :non_final, :final])
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when a page param is passed in." do
|
39
|
+
let(:opts) { {opts_key1: :opts_val1, page: 1} }
|
40
|
+
it "should only return one response" do
|
41
|
+
base_opts = {opts_key1: :opts_val1, per_page: 100, signature: :the_sig}
|
42
|
+
opts1 = {params: base_opts.merge(page: 1)}
|
43
|
+
non_final_response = double(String, headers: {is_last_page: 'false'})
|
44
|
+
RestClient.should_receive(:get).once { non_final_response }
|
45
|
+
@klass.stub(:parse_response_by_url_format).with(non_final_response, :a_url) { {label => [:non_final]} }
|
46
|
+
@klass.send(meth, :a_url, label, opts)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: helix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.4.
|
4
|
+
version: 0.0.4.4.pre
|
5
5
|
prerelease: 8
|
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-09-
|
12
|
+
date: 2013-09-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -69,15 +69,18 @@ files:
|
|
69
69
|
- lib/helix/album.rb
|
70
70
|
- lib/helix/audio_playlist.rb
|
71
71
|
- lib/helix/base.rb
|
72
|
+
- lib/helix/builds_urls.rb
|
72
73
|
- lib/helix/config.rb
|
73
74
|
- lib/helix/document.rb
|
74
75
|
- lib/helix/durationed.rb
|
75
76
|
- lib/helix/exceptions.rb
|
77
|
+
- lib/helix/has_signatures.rb
|
76
78
|
- lib/helix/hash_ext.rb
|
77
79
|
- lib/helix/image.rb
|
78
80
|
- lib/helix/library.rb
|
79
81
|
- lib/helix/media.rb
|
80
82
|
- lib/helix/object_ext.rb
|
83
|
+
- lib/helix/paginates.rb
|
81
84
|
- lib/helix/playlist.rb
|
82
85
|
- lib/helix/restful.rb
|
83
86
|
- lib/helix/statistics.rb
|
@@ -98,6 +101,7 @@ files:
|
|
98
101
|
- spec/image_spec.rb
|
99
102
|
- spec/library_spec.rb
|
100
103
|
- spec/media_spec.rb
|
104
|
+
- spec/paginates_spec.rb
|
101
105
|
- spec/playlist_spec.rb
|
102
106
|
- spec/spec_helper.rb
|
103
107
|
- spec/statistics_spec.rb
|