panda 1.3.0 → 1.4.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.
@@ -0,0 +1,58 @@
1
+ require 'uri'
2
+
3
+ module Panda
4
+ class Config
5
+
6
+ def config
7
+ @config ||= {}
8
+ end
9
+
10
+ [:api_host, :api_port,
11
+ :access_key, :secret_key,
12
+ :api_version, :cloud_id].each do |attr|
13
+ define_method "#{attr}" do |value|
14
+ config["#{attr.to_s}"] = value
15
+ end
16
+
17
+ define_method "#{attr}=" do |value|
18
+ config["#{attr.to_s}"] = value
19
+ end
20
+ end
21
+
22
+ def to_hash
23
+ config
24
+ end
25
+
26
+ def adapter(adapter_name)
27
+ Panda.adapter = adapter_name
28
+ end
29
+
30
+ def adapter=(adapter_name)
31
+ Panda.adapter = adapter_name
32
+ end
33
+
34
+ # Setup connection for Heroku
35
+ def parse_panda_url(panda_url)
36
+ panda_uri = URI.parse(panda_url)
37
+
38
+ config['access_key'] = panda_uri.user
39
+ config['secret_key'] = panda_uri.password
40
+ config['cloud_id'] = panda_uri.path[1..-1]
41
+ config['api_host'] = panda_uri.host
42
+ config['api_port'] = API_PORT
43
+ config
44
+ end
45
+
46
+ # Set the correct api_host for US/EU
47
+ def region(region)
48
+ if(region.to_s == 'us')
49
+ config['api_host'] = US_API_HOST
50
+ elsif(region.to_s == 'eu')
51
+ config['api_host'] = EU_API_HOST
52
+ else
53
+ raise "Region Unknown"
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -1,84 +1,40 @@
1
1
  module Panda
2
+
3
+ API_PORT=443
4
+ US_API_HOST="api.pandastream.com"
5
+ EU_API_HOST="api.eu.pandastream.com"
6
+
2
7
  class Connection
3
- attr_accessor :api_host, :api_port, :access_key, :secret_key, :api_version, :cloud_id, :format
8
+ attr_accessor :api_host, :api_port, :access_key, :secret_key, :api_version, :cloud_id
4
9
 
5
- API_PORT=443
6
- US_API_HOST="api.pandastream.com"
7
- EU_API_HOST="api.eu.pandastream.com"
8
-
9
- def initialize(auth_params={}, options={})
10
- @raise_error = false
10
+ def initialize(auth_params={})
11
11
  @api_version = 2
12
- @format = "hash"
13
-
14
- if auth_params.class == String
15
- self.format = options[:format] || options["format"]
16
- init_from_uri(auth_params)
17
- else
18
- self.format = auth_params[:format] || auth_params["format"]
19
- init_from_hash(auth_params)
20
- end
21
- end
22
-
23
- # Set the correct api_host for US/EU
24
- def region=(region)
25
- if(region.to_s == "us")
26
- self.api_host = US_API_HOST
27
- elsif(region.to_s == "eu")
28
- self.api_host = EU_API_HOST
29
- else
30
- raise "Region Unknown"
31
- end
32
- end
33
-
34
- # Setup connection for Heroku
35
- def heroku=(url)
36
- init_from_uri(url)
12
+ init_from_hash(auth_params)
37
13
  end
38
14
 
39
- # Raise exception on non JSON parsable response if set
40
- def raise_error=(bool)
41
- @raise_error = bool
15
+ def adapter
16
+ @adapter ||= Panda.adapter.new(api_url)
42
17
  end
43
-
44
- # Setup respond type JSON / Hash
45
- def format=(ret_format)
46
- if ret_format
47
- raise "Format unknown" if !["json", "hash"].include?(ret_format.to_s)
48
- @format = ret_format.to_s
49
- end
50
- end
51
-
18
+
52
19
  # Authenticated requests
53
-
54
20
  def get(request_uri, params={})
55
- @connection = RestClient::Resource.new(api_url)
56
- rescue_restclient_exception do
57
- query = signed_query("GET", request_uri, params)
58
- body_of @connection[request_uri + '?' + query].get
59
- end
21
+ sp = signed_params("GET", request_uri, params)
22
+ adapter.get(request_uri, sp)
60
23
  end
61
24
 
62
25
  def post(request_uri, params={})
63
- @connection = RestClient::Resource.new(api_url)
64
- rescue_restclient_exception do
65
- body_of @connection[request_uri].post(signed_params("POST", request_uri, params))
66
- end
26
+ sp = signed_params("POST", request_uri, params)
27
+ adapter.post(request_uri, sp)
67
28
  end
68
29
 
69
30
  def put(request_uri, params={})
70
- @connection = RestClient::Resource.new(api_url)
71
- rescue_restclient_exception do
72
- body_of @connection[request_uri].put(signed_params("PUT", request_uri, params))
73
- end
31
+ sp = signed_params("PUT", request_uri, params)
32
+ adapter.put(request_uri, sp)
74
33
  end
75
34
 
76
35
  def delete(request_uri, params={})
77
- @connection = RestClient::Resource.new(api_url)
78
- rescue_restclient_exception do
79
- query = signed_query("DELETE", request_uri, params)
80
- body_of @connection[request_uri + '?' + query].delete
81
- end
36
+ sp = signed_params("DELETE", request_uri, params)
37
+ adapter.delete(request_uri, sp)
82
38
  end
83
39
 
84
40
  # Signing methods
@@ -88,9 +44,9 @@ module Panda
88
44
 
89
45
  def signed_params(verb, request_uri, params = {}, timestamp_str = nil)
90
46
  auth_params = stringify_keys(params)
91
- auth_params['cloud_id'] = cloud_id
47
+ auth_params['cloud_id'] = cloud_id unless request_uri =~ /^\/clouds/
92
48
  auth_params['access_key'] = access_key
93
- auth_params['timestamp'] = timestamp_str || Time.now.iso8601(6)
49
+ auth_params['timestamp'] = timestamp_str || Time.now.utc.iso8601(6)
94
50
 
95
51
  params_to_sign = auth_params.reject{|k,v| ['file'].include?(k.to_s)}
96
52
  auth_params['signature'] = ApiAuthentication.generate_signature(verb, request_uri, api_host, secret_key, params_to_sign)
@@ -98,10 +54,10 @@ module Panda
98
54
  end
99
55
 
100
56
  def api_url
101
- "#{api_protocol}://#{api_host}:#{api_port}/#{@prefix}"
57
+ "#{api_scheme}://#{api_host}:#{api_port}/#{@prefix}"
102
58
  end
103
59
 
104
- def api_protocol
60
+ def api_scheme
105
61
  api_port == 443 ? 'https' : 'http'
106
62
  end
107
63
 
@@ -109,8 +65,8 @@ module Panda
109
65
  def setup_bucket(params={})
110
66
  granting_params = {
111
67
  :s3_videos_bucket => params[:bucket],
112
- :user_aws_key => params[:access_key],
113
- :user_aws_secret => params[:secret_key]
68
+ :aws_access_key => params[:access_key],
69
+ :aws_secret_key => params[:secret_key]
114
70
  }
115
71
 
116
72
  put("/clouds/#{@cloud_id}.json", granting_params)
@@ -125,61 +81,16 @@ module Panda
125
81
  end
126
82
 
127
83
  private
128
- def stringify_keys(params)
129
- params.inject({}) do |options, (key, value)|
130
- options[key.to_s] = value
131
- options
132
- end
133
- end
134
84
 
135
- def rescue_restclient_exception(&block)
136
- begin
137
- yield
138
- rescue RestClient::Exception => e
139
- format_to(e.http_body)
140
- end
141
- end
142
-
143
- # API change on rest-client 1.4
144
- def body_of(response)
145
- json_response = response.respond_to?(:body) ? response.body : response
146
- format_to(json_response)
147
- end
148
-
149
- def format_to(response)
150
- begin
151
- if self.format == "json"
152
- response
153
- elsif defined?(ActiveSupport::JSON)
154
- ActiveSupport::JSON.decode(response)
155
- else
156
- JSON.parse(response)
157
- end
158
- rescue JSON::ParserError => e
159
- # if not used with PandaResources
160
- # don't raise Service Not Available because
161
- # maybe the host, the url, or anything is wrongly setup
162
- if @raise_error
163
- raise ServiceNotAvailable.new
164
- else
165
- raise e
166
- end
167
- end
168
- end
169
-
170
- def init_from_uri(uri)
171
- heroku_uri = URI.parse(uri)
172
-
173
- @access_key = heroku_uri.user
174
- @secret_key = heroku_uri.password
175
- @cloud_id = heroku_uri.path[1..-1]
176
- @api_host = heroku_uri.host
177
- @api_port = heroku_uri.port
178
- @prefix = "v#{api_version}"
85
+ def stringify_keys(params)
86
+ params.inject({}) do |options, (key, value)|
87
+ options[key.to_s] = value
88
+ options
179
89
  end
90
+ end
180
91
 
181
- def init_from_hash(hash_params)
182
- params = { :api_host => US_API_HOST, :api_port => API_PORT }.merge!(hash_params)
92
+ def init_from_hash(hash_params)
93
+ params = { :api_host => US_API_HOST, :api_port => API_PORT }.merge!(hash_params)
183
94
 
184
95
  @cloud_id = params["cloud_id"] || params[:cloud_id]
185
96
  @access_key = params["access_key"] || params[:access_key]
@@ -187,7 +98,7 @@ module Panda
187
98
  @api_host = params["api_host"] || params[:api_host]
188
99
  @api_port = params["api_port"] || params[:api_port]
189
100
  @prefix = params["prefix_url"] || "v#{api_version}"
190
- end
101
+ end
191
102
  end
192
103
  end
193
104
 
@@ -0,0 +1,17 @@
1
+ module Panda
2
+
3
+ class Error < StandardError; end
4
+
5
+ class APIError < Panda::Error
6
+ def initialize(options)
7
+ super("#{options['error']}: #{options['message']}")
8
+ end
9
+ end
10
+
11
+ class ServiceNotAvailable < Panda::Error
12
+ def initialize
13
+ super("ServiceNotAvailable")
14
+ end
15
+ end
16
+
17
+ end
@@ -2,35 +2,43 @@ module Panda
2
2
  module Builders
3
3
 
4
4
  def self.included(base)
5
- base.extend(CreateBuilder)
6
- base.extend(DeleteBuilder)
5
+ base.extend(ClassMethods)
7
6
  end
8
7
 
9
- module CreateBuilder
8
+ module ClassMethods
10
9
 
11
- def create(attributes)
10
+ def create(attributes={})
12
11
  resource = build_resource(attributes)
12
+ yield resource if block_given?
13
+
13
14
  resource.create
14
15
  resource
15
16
  end
16
17
 
17
- def create!(attributes)
18
+ def create!(attributes={})
18
19
  resource = build_resource(attributes)
20
+ yield resource if block_given?
21
+
19
22
  resource.create!
20
23
  resource
21
24
  end
22
25
 
23
26
  private
27
+
24
28
  def build_resource(attributes)
25
29
  Panda::const_get("#{sti_name}").new(attributes.merge(:cloud_id => cloud.id))
26
30
  end
27
31
  end
28
32
 
29
- module DeleteBuilder
30
- def delete(id)
31
- response = connection.delete(full_object_url(object_url(one_path,{:id =>id})))
32
- response['deleted'] == 'ok'
33
- end
33
+ def create
34
+ raise "Can't create attribute. Already have an id=#{attributes['id']}" if attributes['id']
35
+ uri = replace_pattern_with_self_variables(self.class.many_path)
36
+ response = connection.post(uri, attributes)
37
+ load_and_reset(response)
38
+ end
39
+
40
+ def create!
41
+ create || raise(errors.last)
34
42
  end
35
43
 
36
44
  end
@@ -0,0 +1,25 @@
1
+ module Panda
2
+ module Destroyers
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def delete(id)
11
+ uri = json_path(create_rest_url(one_path,{:id =>id}))
12
+ response = connection.delete(uri)
13
+ response['deleted'] == 'ok'
14
+ end
15
+
16
+ end
17
+
18
+ def delete
19
+ uri = replace_pattern_with_self_variables(self.class.one_path)
20
+ response = connection.delete(uri)
21
+ !!response['deleted']
22
+ end
23
+
24
+ end
25
+ end
@@ -1,16 +1,22 @@
1
1
  module Panda
2
2
  module Finders
3
3
 
4
+ def self.included(base)
5
+ base.extend(FindOne)
6
+ base.extend(FindMany)
7
+ end
8
+
4
9
  module FindOne
5
10
 
6
11
  def find(id)
12
+ raise 'find method requires a correct value' if id.nil? || id == ''
7
13
  find_by_path(one_path, {:id => id})
8
14
  end
9
15
 
10
16
  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)
17
+ rest_url = create_rest_url(url, map)
18
+ params = extract_unmapped_variables(url, map)
19
+ connection.get(rest_url, params)
14
20
  end
15
21
 
16
22
  def find_by_path(url, map={})
@@ -22,7 +28,7 @@ module Panda
22
28
  elsif object['id']
23
29
  kclass.new(object)
24
30
  else
25
- Error.new(object).raise!
31
+ raise APIError.new(object)
26
32
  end
27
33
  end
28
34
 
@@ -1,6 +1,5 @@
1
1
  module Panda
2
2
  module Router
3
- DEFAULT_FORMAT = "json"
4
3
  VAR_PATTERN = /:\w+/
5
4
 
6
5
  def self.included(base)
@@ -31,24 +30,31 @@ module Panda
31
30
  prefix_path + end_path
32
31
  end
33
32
 
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]})
33
+ def create_rest_url(url, map)
34
+ new_url = replace_pattern_with_variables(url, map)
35
+ json_path(new_url)
36
36
  end
37
+
38
+ private
37
39
 
38
- def element_params(url, map)
40
+ def replace_pattern_with_variables(url, map)
41
+ new_url = url.clone
42
+ new_url.gsub(VAR_PATTERN){|key| map[key[1..-1].to_sym] || map[key[1..-1].to_s]}
43
+ end
44
+
45
+ def extract_unmapped_variables(url, map)
39
46
  params = map.clone
40
- url.clone.scan(VAR_PATTERN).map{|key| params.reject!{|k,v| k==key[1..-1] } }
47
+ url.scan(VAR_PATTERN).map{|key| params.reject!{|k,v| k==key[1..-1] } }
41
48
  params
42
49
  end
43
50
 
44
- def full_object_url(url)
45
- url + ".#{DEFAULT_FORMAT}"
51
+ def json_path(uri)
52
+ uri + ".json"
46
53
  end
47
-
48
54
  end
49
55
 
50
- def object_url_map(url)
51
- self.class.full_object_url(url.clone.gsub(VAR_PATTERN) {|key| send(key[1..-1].to_sym)})
56
+ def replace_pattern_with_self_variables(url)
57
+ self.class.create_rest_url(url, attributes)
52
58
  end
53
59
 
54
60
  end