supercast 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/supercast-console.rb +24 -0
- data/lib/supercast/client.rb +552 -0
- data/lib/supercast/data_list.rb +110 -0
- data/lib/supercast/data_object.rb +410 -0
- data/lib/supercast/data_types.rb +21 -0
- data/lib/supercast/errors.rb +73 -0
- data/lib/supercast/operations/create.rb +19 -0
- data/lib/supercast/operations/destroy.rb +42 -0
- data/lib/supercast/operations/list.rb +36 -0
- data/lib/supercast/operations/request.rb +65 -0
- data/lib/supercast/operations/save.rb +87 -0
- data/lib/supercast/resource.rb +79 -0
- data/lib/supercast/resources/channel.rb +9 -0
- data/lib/supercast/resources/creator.rb +11 -0
- data/lib/supercast/resources/episode.rb +12 -0
- data/lib/supercast/resources/invite.rb +10 -0
- data/lib/supercast/resources/role.rb +12 -0
- data/lib/supercast/resources/subscriber.rb +12 -0
- data/lib/supercast/resources/usage_alert.rb +28 -0
- data/lib/supercast/resources.rb +9 -0
- data/lib/supercast/response.rb +48 -0
- data/lib/supercast/singleton.rb +25 -0
- data/lib/supercast/util.rb +318 -0
- data/lib/supercast/version.rb +5 -0
- data/lib/supercast.rb +145 -0
- data/spec/lib/supercast/resource_spec.rb +11 -0
- data/spec/lib/supercast/version_spec.rb +9 -0
- data/spec/lib/supercast_spec.rb +39 -0
- data/spec/spec_helper.rb +111 -0
- metadata +147 -0
@@ -0,0 +1,410 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Supercast
|
4
|
+
class DataObject
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
@@permanent_attributes = Set.new([:id]) # rubocop:disable Style/ClassVars
|
8
|
+
|
9
|
+
# The default :id method is deprecated and isn't useful to us
|
10
|
+
undef :id if method_defined?(:id)
|
11
|
+
|
12
|
+
def initialize(id = nil, opts = {})
|
13
|
+
id, @retrieve_params = Util.normalize_id(id)
|
14
|
+
@opts = Util.normalize_opts(opts)
|
15
|
+
@original_values = {}
|
16
|
+
@values = {}
|
17
|
+
# This really belongs in Resource, but not putting it there allows us
|
18
|
+
# to have a unified inspect method
|
19
|
+
@unsaved_values = Set.new
|
20
|
+
@transient_values = Set.new
|
21
|
+
@values[:id] = id if id
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.construct_from(values, opts = {})
|
25
|
+
values = Supercast::Util.symbolize_names(values)
|
26
|
+
|
27
|
+
# work around protected #initialize_from for now
|
28
|
+
new(values[:id]).send(:initialize_from, values, opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Determines the equality of two Supercast objects. Supercast objects are
|
32
|
+
# considered to be equal if they have the same set of values and each one
|
33
|
+
# of those values is the same.
|
34
|
+
def ==(other)
|
35
|
+
other.is_a?(DataObject) &&
|
36
|
+
@values == other.instance_variable_get(:@values)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Hash equality. As with `#==`, we consider two equivalent Supercast objects
|
40
|
+
# equal.
|
41
|
+
def eql?(other)
|
42
|
+
# Defer to the implementation on `#==`.
|
43
|
+
self == other
|
44
|
+
end
|
45
|
+
|
46
|
+
# As with equality in `#==` and `#eql?`, we hash two Supercast objects to the
|
47
|
+
# same value if they're equivalent objects.
|
48
|
+
def hash
|
49
|
+
@values.hash
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s(*_args)
|
53
|
+
JSON.pretty_generate(to_hash)
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : ''
|
58
|
+
"#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " +
|
59
|
+
JSON.pretty_generate(@values)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Mass assigns attributes on the model.
|
63
|
+
#
|
64
|
+
# This is a version of +update_attributes+ that takes some extra options
|
65
|
+
# for internal use.
|
66
|
+
#
|
67
|
+
# ==== Attributes
|
68
|
+
#
|
69
|
+
# * +values+ - Hash of values to use to update the current attributes of
|
70
|
+
# the object.
|
71
|
+
# * +opts+ - Options for +DataObject+ like an API key that will be reused
|
72
|
+
# on subsequent API calls.
|
73
|
+
#
|
74
|
+
# ==== Options
|
75
|
+
#
|
76
|
+
# * +:dirty+ - Whether values should be initiated as "dirty" (unsaved) and
|
77
|
+
# which applies only to new DataObjects being initiated under this
|
78
|
+
# DataObject. Defaults to true.
|
79
|
+
def update_attributes(values, opts = {}, dirty: true)
|
80
|
+
values.each do |k, v|
|
81
|
+
add_accessors([k], values) unless metaclass.method_defined?(k.to_sym)
|
82
|
+
@values[k] = Util.convert_to_supercast_object(v, opts)
|
83
|
+
dirty_value!(@values[k]) if dirty
|
84
|
+
@unsaved_values.add(k)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def [](key)
|
89
|
+
@values[key.to_sym]
|
90
|
+
end
|
91
|
+
|
92
|
+
def []=(key, value)
|
93
|
+
send(:"#{key}=", value)
|
94
|
+
end
|
95
|
+
|
96
|
+
def keys
|
97
|
+
@values.keys
|
98
|
+
end
|
99
|
+
|
100
|
+
def values
|
101
|
+
@values.values
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_json(_opts)
|
105
|
+
JSON.generate(@values)
|
106
|
+
end
|
107
|
+
|
108
|
+
def as_json(*opts)
|
109
|
+
@values.as_json(*opts)
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_hash
|
113
|
+
maybe_to_hash = lambda do |value|
|
114
|
+
value&.respond_to?(:to_hash) ? value.to_hash : value
|
115
|
+
end
|
116
|
+
|
117
|
+
@values.each_with_object({}) do |(key, value), acc|
|
118
|
+
acc[key] = case value
|
119
|
+
when Array
|
120
|
+
value.map(&maybe_to_hash)
|
121
|
+
else
|
122
|
+
maybe_to_hash.call(value)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def each(&blk)
|
128
|
+
@values.each(&blk)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Sets all keys within the DataObject as unsaved so that they will be
|
132
|
+
# included with an update when #serialize_params is called.
|
133
|
+
def dirty!
|
134
|
+
@unsaved_values = Set.new(@values.keys)
|
135
|
+
|
136
|
+
@values.each_value do |v|
|
137
|
+
dirty_value!(v)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Implements custom encoding for Ruby's Marshal. The data produced by this
|
142
|
+
# method should be comprehendable by #marshal_load.
|
143
|
+
#
|
144
|
+
# This allows us to remove certain features that cannot or should not be
|
145
|
+
# serialized.
|
146
|
+
def marshal_dump
|
147
|
+
# The Client instance in @opts is not serializable and is not
|
148
|
+
# really a property of the DataObject, so we exclude it when
|
149
|
+
# dumping
|
150
|
+
opts = @opts.clone
|
151
|
+
opts.delete(:client)
|
152
|
+
[@values, opts]
|
153
|
+
end
|
154
|
+
|
155
|
+
# Implements custom decoding for Ruby's Marshal. Consumes data that's
|
156
|
+
# produced by #marshal_dump.
|
157
|
+
def marshal_load(data)
|
158
|
+
values, opts = data
|
159
|
+
initialize(values[:id])
|
160
|
+
initialize_from(values, opts)
|
161
|
+
end
|
162
|
+
|
163
|
+
def serialize_params(options = {})
|
164
|
+
update_hash = {}
|
165
|
+
|
166
|
+
@values.each do |k, _v|
|
167
|
+
# There are a few reasons that we may want to add in a parameter for
|
168
|
+
# update:
|
169
|
+
#
|
170
|
+
# 1. The `force` option has been set.
|
171
|
+
# 2. We know that it was modified.
|
172
|
+
#
|
173
|
+
unsaved = @unsaved_values.include?(k)
|
174
|
+
next unless options[:force] || unsaved
|
175
|
+
|
176
|
+
update_hash[k.to_sym] = serialize_params_value(
|
177
|
+
@values[k], @original_values[k], unsaved, options[:force], key: k
|
178
|
+
)
|
179
|
+
end
|
180
|
+
|
181
|
+
# a `nil` that makes it out of `#serialize_params_value` signals an empty
|
182
|
+
# value that we shouldn't appear in the serialized form of the object
|
183
|
+
update_hash.reject! { |_, v| v.nil? }
|
184
|
+
|
185
|
+
update_hash
|
186
|
+
end
|
187
|
+
|
188
|
+
# A protected field is one that doesn't get an accessor assigned to it
|
189
|
+
# (i.e. `obj.public = ...`) and one which is not allowed to be updated via
|
190
|
+
# the class level `Model.update(id, { ... })`.
|
191
|
+
def self.protected_fields
|
192
|
+
[]
|
193
|
+
end
|
194
|
+
|
195
|
+
protected
|
196
|
+
|
197
|
+
def metaclass
|
198
|
+
class << self; self; end
|
199
|
+
end
|
200
|
+
|
201
|
+
def remove_accessors(keys)
|
202
|
+
# not available in the #instance_eval below
|
203
|
+
protected_fields = self.class.protected_fields
|
204
|
+
|
205
|
+
metaclass.instance_eval do
|
206
|
+
keys.each do |k|
|
207
|
+
next if protected_fields.include?(k)
|
208
|
+
next if @@permanent_attributes.include?(k)
|
209
|
+
|
210
|
+
# Remove methods for the accessor's reader and writer.
|
211
|
+
[k, :"#{k}=", :"#{k}?"].each do |method_name|
|
212
|
+
next unless method_defined?(method_name)
|
213
|
+
|
214
|
+
begin
|
215
|
+
remove_method(method_name)
|
216
|
+
rescue NameError
|
217
|
+
warn("WARNING: Unable to remove method `#{method_name}`; " \
|
218
|
+
"if custom, please consider renaming to a name that doesn't " \
|
219
|
+
'collide with an API property name.')
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def add_accessors(keys, values)
|
227
|
+
# not available in the #instance_eval below
|
228
|
+
protected_fields = self.class.protected_fields
|
229
|
+
|
230
|
+
metaclass.instance_eval do
|
231
|
+
keys.each do |k|
|
232
|
+
next if protected_fields.include?(k)
|
233
|
+
next if @@permanent_attributes.include?(k)
|
234
|
+
|
235
|
+
if k == :method
|
236
|
+
# Object#method is a built-in Ruby method that accepts a symbol
|
237
|
+
# and returns the corresponding Method object. Because the API may
|
238
|
+
# also use `method` as a field name, we check the arity of *args
|
239
|
+
# to decide whether to act as a getter or call the parent method.
|
240
|
+
define_method(k) { |*args| args.empty? ? @values[k] : super(*args) }
|
241
|
+
else
|
242
|
+
define_method(k) { @values[k] }
|
243
|
+
end
|
244
|
+
|
245
|
+
define_method(:"#{k}=") do |v|
|
246
|
+
if v == ''
|
247
|
+
raise ArgumentError, "You cannot set #{k} to an empty string. " \
|
248
|
+
'We interpret empty strings as nil in requests. ' \
|
249
|
+
"You may set (object).#{k} = nil to delete the property."
|
250
|
+
end
|
251
|
+
@values[k] = Util.convert_to_supercast_object(v, @opts)
|
252
|
+
dirty_value!(@values[k])
|
253
|
+
@unsaved_values.add(k)
|
254
|
+
end
|
255
|
+
|
256
|
+
define_method(:"#{k}?") { @values[k] } if [FalseClass, TrueClass].include?(values[k].class)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Disabling the cop because it's confused by the fact that the methods are
|
262
|
+
# protected, but we do define `#respond_to_missing?` just below. Hopefully
|
263
|
+
# this is fixed in more recent Rubocop versions.
|
264
|
+
def method_missing(name, *args)
|
265
|
+
# TODO: only allow setting in updateable classes.
|
266
|
+
if name.to_s.end_with?('=')
|
267
|
+
attr = name.to_s[0...-1].to_sym
|
268
|
+
|
269
|
+
# Pull out the assigned value. This is only used in the case of a
|
270
|
+
# boolean value to add a question mark accessor (i.e. `foo?`) for
|
271
|
+
# convenience.
|
272
|
+
val = args.first
|
273
|
+
|
274
|
+
# the second argument is only required when adding boolean accessors
|
275
|
+
add_accessors([attr], attr => val)
|
276
|
+
|
277
|
+
begin
|
278
|
+
mth = method(name)
|
279
|
+
rescue NameError
|
280
|
+
raise NoMethodError,
|
281
|
+
"Cannot set #{attr} on this object. HINT: you can't set: " \
|
282
|
+
"#{@@permanent_attributes.to_a.join(', ')}"
|
283
|
+
end
|
284
|
+
return mth.call(args[0])
|
285
|
+
elsif @values.key?(name)
|
286
|
+
return @values[name]
|
287
|
+
end
|
288
|
+
|
289
|
+
begin
|
290
|
+
super
|
291
|
+
rescue NoMethodError => e
|
292
|
+
# If we notice the accessed name if our set of transient values we can
|
293
|
+
# give the user a slightly more helpful error message. If not, just
|
294
|
+
# raise right away.
|
295
|
+
raise unless @transient_values.include?(name)
|
296
|
+
|
297
|
+
raise NoMethodError,
|
298
|
+
e.message + ". HINT: The '#{name}' attribute was set in the " \
|
299
|
+
'past, however. It was then wiped when refreshing the object ' \
|
300
|
+
"with the result returned by Supercast's API, probably as a " \
|
301
|
+
'result of a save(). The attributes currently available on ' \
|
302
|
+
"this object are: #{@values.keys.join(', ')}"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def respond_to_missing?(symbol, include_private = false)
|
307
|
+
@values&.key?(symbol) || super
|
308
|
+
end
|
309
|
+
|
310
|
+
# Re-initializes the object based on a hash of values (usually one that's
|
311
|
+
# come back from an API call). Adds or removes value accessors as necessary
|
312
|
+
# and updates the state of internal data.
|
313
|
+
#
|
314
|
+
# Protected on purpose! Please do not expose.
|
315
|
+
#
|
316
|
+
# ==== Options
|
317
|
+
#
|
318
|
+
# * +:values:+ Hash used to update accessors and values.
|
319
|
+
# * +:opts:+ Options for DataObject like an API key.
|
320
|
+
def initialize_from(values, opts)
|
321
|
+
@opts = Util.normalize_opts(opts)
|
322
|
+
|
323
|
+
# the `#send` is here so that we can keep this method private
|
324
|
+
@original_values = self.class.send(:deep_copy, values)
|
325
|
+
|
326
|
+
removed = Set.new(@values.keys - values.keys)
|
327
|
+
added = Set.new(values.keys - @values.keys)
|
328
|
+
|
329
|
+
remove_accessors(removed)
|
330
|
+
add_accessors(added, values)
|
331
|
+
|
332
|
+
removed.each do |k|
|
333
|
+
@values.delete(k)
|
334
|
+
@transient_values.add(k)
|
335
|
+
@unsaved_values.delete(k)
|
336
|
+
end
|
337
|
+
|
338
|
+
update_attributes(values, opts, dirty: false)
|
339
|
+
|
340
|
+
values.each_key do |k|
|
341
|
+
@transient_values.delete(k)
|
342
|
+
@unsaved_values.delete(k)
|
343
|
+
end
|
344
|
+
|
345
|
+
self
|
346
|
+
end
|
347
|
+
|
348
|
+
def serialize_params_value(value, original, _unsaved, force)
|
349
|
+
if value.nil?
|
350
|
+
''
|
351
|
+
elsif value.is_a?(Array)
|
352
|
+
update = value.map { |v| serialize_params_value(v, nil, true, force) }
|
353
|
+
|
354
|
+
# This prevents an array that's unchanged from being resent.
|
355
|
+
update if update != serialize_params_value(original, nil, true, force)
|
356
|
+
|
357
|
+
# Handle a Hash for now, but in the long run we should be able to
|
358
|
+
# eliminate all places where hashes are stored as values internally by
|
359
|
+
# making sure any time one is set, we convert it to a DataObject. This
|
360
|
+
# will simplify our model by making data within an object more
|
361
|
+
# consistent.
|
362
|
+
#
|
363
|
+
# For now, you can still run into a hash if someone appends one to an
|
364
|
+
# existing array being held by a DataObject. This could happen for
|
365
|
+
# example by appending a new hash onto `additional_owners` for an
|
366
|
+
# account.
|
367
|
+
elsif value.is_a?(Hash)
|
368
|
+
Util.convert_to_supercast_object(value, @opts).serialize_params
|
369
|
+
elsif value.is_a?(DataObject)
|
370
|
+
value.serialize_params(force: force)
|
371
|
+
else
|
372
|
+
value
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
# Produces a deep copy of the given object including support for arrays,
|
377
|
+
# hashes, and DataObjects.
|
378
|
+
private_class_method def self.deep_copy(obj)
|
379
|
+
case obj
|
380
|
+
when Array
|
381
|
+
obj.map { |e| deep_copy(e) }
|
382
|
+
when Hash
|
383
|
+
obj.each_with_object({}) do |(k, v), copy|
|
384
|
+
copy[k] = deep_copy(v)
|
385
|
+
copy
|
386
|
+
end
|
387
|
+
when DataObject
|
388
|
+
obj.class.construct_from(
|
389
|
+
deep_copy(obj.instance_variable_get(:@values)),
|
390
|
+
obj.instance_variable_get(:@opts).select do |k, _v|
|
391
|
+
Util::OPTS_COPYABLE.include?(k)
|
392
|
+
end
|
393
|
+
)
|
394
|
+
else
|
395
|
+
obj
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
private
|
400
|
+
|
401
|
+
def dirty_value!(value)
|
402
|
+
case value
|
403
|
+
when Array
|
404
|
+
value.map { |v| dirty_value!(v) }
|
405
|
+
when DataObject
|
406
|
+
value.dirty!
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Supercast
|
4
|
+
module DataTypes
|
5
|
+
def self.object_names_to_classes
|
6
|
+
{
|
7
|
+
# data structures
|
8
|
+
DataList::OBJECT_NAME => DataList,
|
9
|
+
|
10
|
+
# business objects
|
11
|
+
Channel::OBJECT_NAME => Channel,
|
12
|
+
Creator::OBJECT_NAME => Creator,
|
13
|
+
Episode::OBJECT_NAME => Episode,
|
14
|
+
Invite::OBJECT_NAME => Invite,
|
15
|
+
Role::OBJECT_NAME => Role,
|
16
|
+
Subscriber::OBJECT_NAME => Subscriber,
|
17
|
+
UsageAlert::OBJECT_NAME => UsageAlert
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Supercast
|
4
|
+
# SupercastError is the base error from which all other more specific Supercast
|
5
|
+
# errors derive.
|
6
|
+
class SupercastError < StandardError
|
7
|
+
attr_reader :message
|
8
|
+
|
9
|
+
# Response contains a SupercastResponse object that has some basic information
|
10
|
+
# about the response that conveyed the error.
|
11
|
+
attr_accessor :response
|
12
|
+
|
13
|
+
attr_reader :code
|
14
|
+
attr_reader :http_body
|
15
|
+
attr_reader :http_headers
|
16
|
+
attr_reader :http_status
|
17
|
+
attr_reader :json_body # equivalent to #data
|
18
|
+
|
19
|
+
# Initializes a SupercastError.
|
20
|
+
def initialize(message = nil, http_status: nil, http_body: nil, json_body: nil, http_headers: nil, code: nil)
|
21
|
+
@message = message
|
22
|
+
@http_status = http_status
|
23
|
+
@http_body = http_body
|
24
|
+
@http_headers = http_headers || {}
|
25
|
+
@json_body = json_body
|
26
|
+
@code = code
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
status_string = @http_status.nil? ? '' : "(Status #{@http_status}) "
|
31
|
+
"#{status_string}#{@message}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# AuthenticationError is raised when invalid credentials are used to connect
|
36
|
+
# to Supercast's servers.
|
37
|
+
class AuthenticationError < SupercastError
|
38
|
+
end
|
39
|
+
|
40
|
+
# APIConnectionError is raised in the event that the SDK can't connect to
|
41
|
+
# Supercast's servers. That can be for a variety of different reasons such as a
|
42
|
+
# downed network
|
43
|
+
class APIConnectionError < SupercastError
|
44
|
+
end
|
45
|
+
|
46
|
+
# APIError is a generic error that may be raised in cases where none of the
|
47
|
+
# other named errors cover the problem. It could also be raised in the case
|
48
|
+
# that a new error has been introduced in the API, but this version of the
|
49
|
+
# Ruby SDK doesn't know how to handle it.
|
50
|
+
class APIError < SupercastError
|
51
|
+
end
|
52
|
+
|
53
|
+
# InvalidRequestError is raised when a request is initiated with invalid
|
54
|
+
# parameters.
|
55
|
+
class InvalidRequestError < SupercastError
|
56
|
+
def initialize(message, http_status: nil, http_body: nil, json_body: nil, http_headers: nil, code: nil)
|
57
|
+
super(message, http_status: http_status, http_body: http_body,
|
58
|
+
json_body: json_body, http_headers: http_headers,
|
59
|
+
code: code)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# PermissionError is raised in cases where access was attempted on a resource
|
64
|
+
# that wasn't allowed.
|
65
|
+
class PermissionError < SupercastError
|
66
|
+
end
|
67
|
+
|
68
|
+
# RateLimitError is raised in cases where an account is putting too much load
|
69
|
+
# on Supercast's API servers (usually by performing too many requests). Please
|
70
|
+
# back off on request rate.
|
71
|
+
class RateLimitError < SupercastError
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Supercast
|
4
|
+
module Operations
|
5
|
+
module Create
|
6
|
+
# Creates an API resource.
|
7
|
+
#
|
8
|
+
# ==== Attributes
|
9
|
+
#
|
10
|
+
# * +params+ - A hash of parameters to pass to the API
|
11
|
+
# * +opts+ - A Hash of additional options (separate from the params /
|
12
|
+
# object values) to be added to the request.
|
13
|
+
def create(params = {}, opts = {})
|
14
|
+
resp, opts = request(:post, resource_url, Hash[object_name => params], opts)
|
15
|
+
Util.convert_to_supercast_object(resp.data, opts)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Supercast
|
4
|
+
module Operations
|
5
|
+
module Destroy
|
6
|
+
module ClassMethods
|
7
|
+
# Deletes an API resource
|
8
|
+
#
|
9
|
+
# Deletes the identified resource with the passed in parameters.
|
10
|
+
#
|
11
|
+
# ==== Attributes
|
12
|
+
#
|
13
|
+
# * +id+ - ID of the resource to delete.
|
14
|
+
# * +params+ - A hash of parameters to pass to the API
|
15
|
+
# * +opts+ - A Hash of additional options (separate from the params /
|
16
|
+
# object values) to be added to the request.
|
17
|
+
def destroy(id, params = {}, opts = {})
|
18
|
+
request(:delete, "#{resource_url}/#{id}", params, opts)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Deletes an API resource instance
|
24
|
+
#
|
25
|
+
# Deletes the instance resource with the passed in parameters.
|
26
|
+
#
|
27
|
+
# ==== Attributes
|
28
|
+
#
|
29
|
+
# * +params+ - A hash of parameters to pass to the API
|
30
|
+
# * +opts+ - A Hash of additional options (separate from the params /
|
31
|
+
# object values) to be added to the request.
|
32
|
+
def destroy(params = {}, opts = {})
|
33
|
+
request(:delete, resource_url, params, opts)
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.included(base)
|
38
|
+
base.extend(ClassMethods)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Supercast
|
4
|
+
module Operations
|
5
|
+
module List
|
6
|
+
# Lists an API resource
|
7
|
+
#
|
8
|
+
# ==== Attributes
|
9
|
+
#
|
10
|
+
# * +filters+ - A hash of filters to pass to the API
|
11
|
+
# * +opts+ - A Hash of additional options (separate from the params /
|
12
|
+
# object values) to be added to the request.
|
13
|
+
def list(filters = {}, opts = {})
|
14
|
+
opts = Util.normalize_opts(opts)
|
15
|
+
|
16
|
+
resp, opts = request(:get, resource_url, filters, opts)
|
17
|
+
obj = DataList.construct_from({
|
18
|
+
data: resp.data,
|
19
|
+
page: resp.http_headers['x-page'].to_i,
|
20
|
+
per_page: resp.http_headers['x-per-page'].to_i,
|
21
|
+
total: resp.http_headers['x-total'].to_i
|
22
|
+
}, opts)
|
23
|
+
|
24
|
+
# set filters so that we can fetch the same limit, expansions, and
|
25
|
+
# predicates when accessing the next and previous pages
|
26
|
+
#
|
27
|
+
# just for general cleanliness, remove any paging options
|
28
|
+
obj.filters = filters.dup
|
29
|
+
obj.filters.delete(:ending_before)
|
30
|
+
obj.filters.delete(:starting_after)
|
31
|
+
|
32
|
+
obj
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Supercast
|
4
|
+
module Operations
|
5
|
+
module Request
|
6
|
+
module ClassMethods
|
7
|
+
# Invokes an HTTP request via the Supercast Client for
|
8
|
+
# manipulating an API resource.
|
9
|
+
#
|
10
|
+
# ==== Attributes
|
11
|
+
#
|
12
|
+
# * +method+ - A symbol for the HTTP verb to use in the request
|
13
|
+
# * +url+ - A string which dictates what API endpoint URL to hit
|
14
|
+
# * +params+ - A hash of parameters to pass to the API
|
15
|
+
# * +opts+ - A Hash of additional options (separate from the params /
|
16
|
+
# object values) to be added to the request.
|
17
|
+
def request(method, url, params = {}, opts = {})
|
18
|
+
warn_on_opts_in_params(params)
|
19
|
+
|
20
|
+
opts = Util.normalize_opts(opts)
|
21
|
+
opts[:client] ||= Client.active_client
|
22
|
+
|
23
|
+
headers = opts.clone
|
24
|
+
api_key = headers.delete(:api_key)
|
25
|
+
api_base = headers.delete(:api_base)
|
26
|
+
client = headers.delete(:client)
|
27
|
+
# Assume all remaining opts must be headers
|
28
|
+
|
29
|
+
resp, opts[:api_key] = client.execute_request(
|
30
|
+
method, url,
|
31
|
+
api_base: api_base, api_key: api_key,
|
32
|
+
headers: headers, params: params
|
33
|
+
)
|
34
|
+
|
35
|
+
# Hash#select returns an array before 1.9
|
36
|
+
opts_to_persist = {}
|
37
|
+
opts.each do |k, v|
|
38
|
+
opts_to_persist[k] = v if Util::OPTS_PERSISTABLE.include?(k)
|
39
|
+
end
|
40
|
+
|
41
|
+
[resp, opts_to_persist]
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def warn_on_opts_in_params(params)
|
47
|
+
Util::OPTS_USER_SPECIFIED.each do |opt|
|
48
|
+
warn("WARNING: #{opt} should be in opts instead of params.") if params.key?(opt)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.included(base)
|
54
|
+
base.extend(ClassMethods)
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def request(method, url, params = {}, opts = {})
|
60
|
+
opts = @opts.merge(Util.normalize_opts(opts))
|
61
|
+
self.class.request(method, url, params, opts)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|