panda 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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