panda 0.6.4 → 1.0.0
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/Gemfile +10 -0
- data/README.md +388 -21
- data/Rakefile +4 -17
- data/VERSION +1 -1
- data/lib/panda.rb +21 -1
- data/lib/panda/api_authentication.rb +4 -4
- data/lib/panda/base.rb +102 -0
- data/lib/panda/connection.rb +189 -0
- data/lib/panda/error.rb +29 -0
- data/lib/panda/modules/associations.rb +55 -0
- data/lib/panda/modules/builders.rb +34 -0
- data/lib/panda/modules/cloud_connection.rb +7 -0
- data/lib/panda/modules/finders.rb +62 -0
- data/lib/panda/modules/router.rb +55 -0
- data/lib/panda/modules/short_status.rb +13 -0
- data/lib/panda/modules/updatable.rb +27 -0
- data/lib/panda/panda.rb +21 -170
- data/lib/panda/proxies/encoding_scope.rb +48 -0
- data/lib/panda/proxies/profile_scope.rb +7 -0
- data/lib/panda/proxies/proxy.rb +25 -0
- data/lib/panda/proxies/scope.rb +87 -0
- data/lib/panda/proxies/video_scope.rb +28 -0
- data/lib/panda/resources/cloud.rb +49 -0
- data/lib/panda/resources/encoding.rb +30 -0
- data/lib/panda/resources/profile.rb +22 -0
- data/lib/panda/resources/resource.rb +52 -0
- data/lib/panda/resources/video.rb +13 -0
- data/panda.gemspec +36 -12
- data/spec/cloud_spec.rb +80 -0
- data/spec/encoding_spec.rb +232 -0
- data/spec/heroku_spec.rb +22 -0
- data/spec/panda_spec.rb +17 -4
- data/spec/profile_spec.rb +117 -0
- data/spec/spec_helper.rb +8 -1
- data/spec/video_spec.rb +305 -0
- metadata +33 -23
- data/log/debug.log +0 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module Panda
|
2
|
+
module Builders
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(CreateBuilder)
|
6
|
+
base.extend(DeleteBuilder)
|
7
|
+
end
|
8
|
+
|
9
|
+
module CreateBuilder
|
10
|
+
|
11
|
+
def create(attributes)
|
12
|
+
if attr_id=(attributes[:id] || attributes['id'])
|
13
|
+
raise "Can't create attribute. Already have an id=#{attr_id}"
|
14
|
+
end
|
15
|
+
|
16
|
+
response = connection.post(full_object_url(many_path), attributes)
|
17
|
+
Panda::const_get("#{end_class_name}").new(response)
|
18
|
+
end
|
19
|
+
|
20
|
+
def create!(attributes)
|
21
|
+
create(attributes) || raise(self.error.first.to_s)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
module DeleteBuilder
|
27
|
+
def delete(id)
|
28
|
+
response = connection.delete(full_object_url(object_url(one_path,{:id =>id})))
|
29
|
+
response['deleted'] == 'ok'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Panda
|
2
|
+
module Finders
|
3
|
+
|
4
|
+
module FindOne
|
5
|
+
|
6
|
+
def find(id)
|
7
|
+
find_by_path(one_path, {:id => id})
|
8
|
+
end
|
9
|
+
|
10
|
+
def find_object_by_path(url, map={})
|
11
|
+
full_url = object_url(url, map)
|
12
|
+
params = element_params(url, map)
|
13
|
+
self.connection.get(full_url, params)
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_by_path(url, map={})
|
17
|
+
object = find_object_by_path(url, map)
|
18
|
+
kclass = Panda::const_get("#{end_class_name}")
|
19
|
+
|
20
|
+
if object.is_a?(Array)
|
21
|
+
object.map{|o| kclass.new(o)}
|
22
|
+
elsif object["id"]
|
23
|
+
kclass.new(object)
|
24
|
+
else
|
25
|
+
Error.new(object).raise!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
module FindMany
|
32
|
+
|
33
|
+
def find_by(map)
|
34
|
+
all(map).first
|
35
|
+
end
|
36
|
+
|
37
|
+
def all(map={})
|
38
|
+
find_by_path(many_path, map)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def find_all_by_has_many(relation_name, relation_value)
|
44
|
+
map = {}
|
45
|
+
map[relation_name.to_sym] = relation_value
|
46
|
+
has_many_path = build_hash_many_path(many_path, relation_name)
|
47
|
+
find_by_path(has_many_path, map)
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(method_symbol, *arguments)
|
51
|
+
method_name = method_symbol.to_s
|
52
|
+
if method_name =~ /^find_all_by_([_a-zA-Z]\w*)$/
|
53
|
+
find_all_by_has_many($1, arguments.pop)
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Panda
|
2
|
+
module Router
|
3
|
+
DEFAULT_FORMAT = "json"
|
4
|
+
VAR_PATTERN = /:\w+/
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def resource_path
|
13
|
+
@url || "/#{end_class_name.downcase}s"
|
14
|
+
end
|
15
|
+
|
16
|
+
def match(url)
|
17
|
+
@url = url
|
18
|
+
end
|
19
|
+
|
20
|
+
def many_path
|
21
|
+
resource_path
|
22
|
+
end
|
23
|
+
|
24
|
+
def one_path
|
25
|
+
resource_path + "/:id"
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_hash_many_path(end_path, relation_attr)
|
29
|
+
relation_class_name = relation_attr[0..relation_attr.rindex("_id")-1].capitalize
|
30
|
+
prefix_path = Panda::const_get(relation_class_name).resource_path + "/:" + relation_attr
|
31
|
+
prefix_path + end_path
|
32
|
+
end
|
33
|
+
|
34
|
+
def object_url(url, map)
|
35
|
+
full_object_url(url.clone.gsub(VAR_PATTERN){|key| map[key[1..-1].to_sym] || map[key[1..-1].to_s]})
|
36
|
+
end
|
37
|
+
|
38
|
+
def element_params(url, map)
|
39
|
+
params = map.clone
|
40
|
+
url.clone.scan(VAR_PATTERN).map{|key| params.reject!{|k,v| k==key[1..-1] } }
|
41
|
+
params
|
42
|
+
end
|
43
|
+
|
44
|
+
def full_object_url(url)
|
45
|
+
url + ".#{DEFAULT_FORMAT}"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def object_url_map(url)
|
51
|
+
self.class.full_object_url(url.clone.gsub(VAR_PATTERN) {|key| send(key[1..-1].to_sym)})
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Panda
|
2
|
+
module Updatable
|
3
|
+
|
4
|
+
def save
|
5
|
+
new? ? create : update
|
6
|
+
end
|
7
|
+
|
8
|
+
def save!
|
9
|
+
save || raise("Resource invalid")
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_attribute(name, value)
|
13
|
+
self.send("#{name}=".to_sym, value)
|
14
|
+
self.save
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_attributes(attributes)
|
18
|
+
load(attributes) && save
|
19
|
+
end
|
20
|
+
|
21
|
+
def update
|
22
|
+
response = connection.put(object_url_map(self.class.one_path), @changed_attributes)
|
23
|
+
load_response(response) ? (@changed_attributes = {}; true) : false
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/panda/panda.rb
CHANGED
@@ -1,187 +1,38 @@
|
|
1
1
|
require 'restclient'
|
2
|
+
require 'forwardable'
|
2
3
|
require 'json' unless defined?(ActiveSupport::JSON)
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@@connection = Connection.new(auth_params, options)
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.get(request_uri, params={})
|
11
|
-
connection.get(request_uri, params)
|
12
|
-
end
|
5
|
+
module Panda
|
6
|
+
class << self
|
7
|
+
extend Forwardable
|
13
8
|
|
14
|
-
|
15
|
-
connection
|
16
|
-
end
|
9
|
+
attr_reader :cloud, :clouds
|
10
|
+
attr_reader :connection
|
17
11
|
|
18
|
-
|
19
|
-
connection.put(request_uri, params)
|
20
|
-
end
|
12
|
+
def_delegators :connection, :get, :post, :put, :delete, :api_url, :setup_bucket, :signed_params
|
21
13
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.setup_bucket(params={})
|
27
|
-
connection.setup_bucket(params)
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.authentication_params(*params)
|
31
|
-
raise "Method deprecated. Please use signed_params instead."
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.signed_params(verb, request_uri, params = {}, timestamp_str = nil)
|
35
|
-
connection.signed_params(verb, request_uri, params, timestamp_str)
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def self.connection
|
41
|
-
if defined?(@@connection)
|
42
|
-
@@connection
|
43
|
-
else
|
44
|
-
raise "Not connected. Please connect! first."
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class Connection
|
49
|
-
attr_reader :api_host, :api_port, :access_key, :secret_key, :api_version, :format
|
50
|
-
|
51
|
-
DEFAULT_API_PORT=80
|
52
|
-
DEFAULT_API_HOST="api.pandastream.com"
|
53
|
-
|
54
|
-
def initialize(auth_params, options={})
|
55
|
-
@api_version = 2
|
56
|
-
@format = "hash"
|
57
|
-
|
58
|
-
if auth_params.class == String
|
59
|
-
self.format = options[:format] || options["format"]
|
60
|
-
init_from_url(auth_params)
|
61
|
-
else
|
62
|
-
self.format = auth_params[:format] || auth_params["format"]
|
63
|
-
init_from_hash(auth_params)
|
64
|
-
end
|
65
|
-
|
66
|
-
@connection = RestClient::Resource.new(api_url)
|
67
|
-
end
|
68
|
-
|
69
|
-
def format=(ret_format)
|
70
|
-
if ret_format
|
71
|
-
raise "Format unknown" if !["json", "hash"].include?(ret_format.to_s)
|
72
|
-
@format = ret_format.to_s
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def get(request_uri, params={})
|
77
|
-
rescue_restclient_exception do
|
78
|
-
query = signed_query("GET", request_uri, params)
|
79
|
-
body_of @connection[request_uri + '?' + query].get
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def post(request_uri, params={})
|
84
|
-
rescue_restclient_exception do
|
85
|
-
body_of @connection[request_uri].post(signed_params("POST", request_uri, params))
|
86
|
-
end
|
87
|
-
end
|
14
|
+
def configure(auth_params=nil)
|
15
|
+
@clouds = {}
|
88
16
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
def delete(request_uri, params={})
|
96
|
-
rescue_restclient_exception do
|
97
|
-
query = signed_query("DELETE", request_uri, params)
|
98
|
-
body_of @connection[request_uri + '?' + query].delete
|
17
|
+
if auth_params
|
18
|
+
connect!(auth_params)
|
19
|
+
else
|
20
|
+
yield @connection = Connection.new
|
99
21
|
end
|
100
|
-
end
|
101
22
|
|
102
|
-
|
103
|
-
|
23
|
+
@connection.raise_error=true
|
24
|
+
@connection.format = :hash
|
25
|
+
@cloud = Cloud::new(:id => @connection.cloud_id)
|
104
26
|
end
|
105
|
-
|
106
|
-
def signed_params(verb, request_uri, params = {}, timestamp_str = nil)
|
107
|
-
auth_params = stringify_keys(params)
|
108
|
-
auth_params['cloud_id'] = @cloud_id
|
109
|
-
auth_params['access_key'] = @access_key
|
110
|
-
auth_params['timestamp'] = timestamp_str || Time.now.iso8601(6)
|
111
27
|
|
112
|
-
|
113
|
-
|
114
|
-
auth_params
|
28
|
+
def connect!(auth_params, options={})
|
29
|
+
@connection = Connection.new(auth_params, options)
|
115
30
|
end
|
116
31
|
|
117
|
-
def
|
118
|
-
"
|
32
|
+
def connection
|
33
|
+
raise "Not connected. Please connect! first." unless @connection
|
34
|
+
@connection
|
119
35
|
end
|
120
36
|
|
121
|
-
def setup_bucket(params={})
|
122
|
-
granting_params = { :s3_videos_bucket => params[:bucket], :user_aws_key => params[:access_key], :user_aws_secret => params[:secret_key] }
|
123
|
-
put("/clouds/#{@cloud_id}.json", granting_params)
|
124
|
-
end
|
125
|
-
|
126
|
-
private
|
127
|
-
def stringify_keys(params)
|
128
|
-
params.inject({}) do |options, (key, value)|
|
129
|
-
options[key.to_s] = value
|
130
|
-
options
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def rescue_restclient_exception(&block)
|
135
|
-
begin
|
136
|
-
yield
|
137
|
-
rescue RestClient::Exception => e
|
138
|
-
format_to(e.http_body)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
# API change on rest-client 1.4
|
143
|
-
def body_of(response)
|
144
|
-
json_response = response.respond_to?(:body) ? response.body : response
|
145
|
-
format_to(json_response)
|
146
|
-
end
|
147
|
-
|
148
|
-
def format_to(response)
|
149
|
-
if self.format == "json"
|
150
|
-
response
|
151
|
-
|
152
|
-
elsif defined?(ActiveSupport::JSON)
|
153
|
-
ActiveSupport::JSON.decode(response)
|
154
|
-
|
155
|
-
else
|
156
|
-
JSON.parse(response)
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def init_from_url(url)
|
161
|
-
params = url.scan(/http:\/\/([^:@]+):([^:@]+)@([^:@]+)(:[\d]+)?\/([^:@]+)$/).flatten
|
162
|
-
@access_key = params[0]
|
163
|
-
@secret_key = params[1]
|
164
|
-
@cloud_id = params[4]
|
165
|
-
@api_host = params[2]
|
166
|
-
|
167
|
-
if params[3]
|
168
|
-
@api_port = params[3][1..-1]
|
169
|
-
else
|
170
|
-
@api_port = DEFAULT_API_PORT
|
171
|
-
end
|
172
|
-
@prefix = "v#{@api_version}"
|
173
|
-
|
174
|
-
end
|
175
|
-
|
176
|
-
def init_from_hash(hash_params)
|
177
|
-
params = { :api_host => DEFAULT_API_HOST, :api_port => DEFAULT_API_PORT }.merge(hash_params)
|
178
|
-
|
179
|
-
@cloud_id = params["cloud_id"] || params[:cloud_id]
|
180
|
-
@access_key = params["access_key"] || params[:access_key]
|
181
|
-
@secret_key = params["secret_key"] || params[:secret_key]
|
182
|
-
@api_host = params["api_host"] || params[:api_host]
|
183
|
-
@api_port = params["api_port"] || params[:api_port]
|
184
|
-
@prefix = params["prefix_url"] || "v#{@api_version}"
|
185
|
-
end
|
186
37
|
end
|
187
38
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Panda
|
2
|
+
class EncodingScope < Scope
|
3
|
+
|
4
|
+
def initialize(parent)
|
5
|
+
super(parent, Encoding)
|
6
|
+
end
|
7
|
+
|
8
|
+
def non_delegate_methods
|
9
|
+
super + [:status, :profile_id, :profile_name, :video, :page, :per_page]
|
10
|
+
end
|
11
|
+
|
12
|
+
def page(this_page)
|
13
|
+
@scoped_attributes[:page] = this_page
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def per_page(this_per_page)
|
18
|
+
@scoped_attributes[:per_page] = this_per_page
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def video(this_video_id)
|
23
|
+
@scoped_attributes[:video_id] = this_video_id
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def status(this_status)
|
28
|
+
@scoped_attributes[:status] = this_status
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def profile(this_profile_id)
|
33
|
+
@scoped_attributes[:profile_id] = this_profile_id
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def profile_name(this_profile_name)
|
38
|
+
@scoped_attributes[:profile_name] = this_profile_name
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_by_profile_name(this_profile_name)
|
43
|
+
@scoped_attributes[:profile_name] = this_profile_name
|
44
|
+
trigger_request.first
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|