cloudinary 1.0.66 → 1.0.67

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ = Version 1.0.67 - 2014-01-09
2
+ * Support specifying face coordinates in upload API.
3
+ * Support specifying context (currently alt and caption) in upload API and returning context in API.
4
+ * Support specifying allowed image formats in upload API.
5
+ * Support listing resources in admin API by multiple public IDs.
6
+ * Send User-Agent header with client library version in API request.
7
+ * Support for signed-URLs to override restricted dynamic URLs.
8
+
1
9
  = Version 1.0.66 - 2013-11-14
2
10
  * Support overwrite flag in upload
3
11
  * Support tags flag in resources_by_tag
data/lib/cloudinary.rb CHANGED
@@ -25,7 +25,9 @@ module Cloudinary
25
25
  CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"
26
26
  AKAMAI_SHARED_CDN = "res.cloudinary.com"
27
27
  OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"
28
- SHARED_CDN = AKAMAI_SHARED_CDN
28
+ SHARED_CDN = AKAMAI_SHARED_CDN
29
+
30
+ USER_AGENT = "cld-ruby-" + VERSION
29
31
 
30
32
  FORMAT_ALIASES = {
31
33
  "jpeg" => "jpg",
@@ -36,13 +36,20 @@ class Cloudinary::Api
36
36
  type = options[:type]
37
37
  uri = "resources/#{resource_type}"
38
38
  uri += "/#{type}" if !type.blank?
39
- call_api(:get, uri, only(options, :next_cursor, :max_results, :prefix, :tags), options)
39
+ call_api(:get, uri, only(options, :next_cursor, :max_results, :prefix, :tags, :context), options)
40
40
  end
41
41
 
42
42
  def self.resources_by_tag(tag, options={})
43
43
  resource_type = options[:resource_type] || "image"
44
44
  uri = "resources/#{resource_type}/tags/#{tag}"
45
- call_api(:get, uri, only(options, :next_cursor, :max_results, :tags), options)
45
+ call_api(:get, uri, only(options, :next_cursor, :max_results, :tags, :context), options)
46
+ end
47
+
48
+ def self.resources_by_ids(public_ids, options={})
49
+ resource_type = options[:resource_type] || "image"
50
+ type = options[:type] || "upload"
51
+ uri = "resources/#{resource_type}/#{type}"
52
+ call_api(:get, uri, only(options, :tags, :context).merge(public_ids: public_ids), options)
46
53
  end
47
54
 
48
55
  def self.resource(public_id, options={})
@@ -126,7 +133,7 @@ class Cloudinary::Api
126
133
  # Add authentication
127
134
  api_url.sub!(%r(^(https?://)), "\\1#{api_key}:#{api_secret}@")
128
135
 
129
- RestClient::Request.execute(:method => method, :url => api_url, :payload => params.reject{|k, v| v.nil? || v==""}, :timeout=>60) do
136
+ RestClient::Request.execute(:method => method, :url => api_url, :payload => params.reject{|k, v| v.nil? || v==""}, :timeout=>60, :headers => {"User-Agent" => Cloudinary::USER_AGENT}) do
130
137
  |response, request, tmpresult|
131
138
  return Response.new(response) if response.code == 200
132
139
  exception_class = case response.code
@@ -38,7 +38,10 @@ class Cloudinary::Uploader
38
38
  :eager_async=>Cloudinary::Utils.as_safe_bool(options[:eager_async]),
39
39
  :proxy=>options[:proxy],
40
40
  :folder=>options[:folder],
41
- :tags=>options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(",")}
41
+ :tags=>options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
42
+ :allowed_formats => Cloudinary::Utils.build_array(options[:allowed_formats]).join(","),
43
+ :context => Cloudinary::Utils.encode_hash(options[:context]),
44
+ :face_coordinates => Cloudinary::Utils.encode_double_array(options[:face_coordinates])}
42
45
  params
43
46
  end
44
47
 
@@ -98,7 +101,8 @@ class Cloudinary::Uploader
98
101
  :callback=> options[:callback],
99
102
  :eager=>build_eager(options[:eager]),
100
103
  :headers=>build_custom_headers(options[:headers]),
101
- :tags=>options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(",")
104
+ :tags=>options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
105
+ :face_coordinates => options[:face_coordinates] && Cloudinary::Utils.encode_double_array(options[:face_coordinates])
102
106
  }
103
107
  end
104
108
  end
@@ -205,7 +209,7 @@ class Cloudinary::Uploader
205
209
 
206
210
  api_url = Cloudinary::Utils.cloudinary_api_url(action, options)
207
211
 
208
- RestClient::Request.execute(:method => :post, :url => api_url, :payload => params.reject{|k, v| v.nil? || v==""}, :timeout=>60) do
212
+ RestClient::Request.execute(:method => :post, :url => api_url, :payload => params.reject{|k, v| v.nil? || v==""}, :timeout=>60, :headers => {"User-Agent" => Cloudinary::USER_AGENT}) do
209
213
  |response, request, tmpresult|
210
214
  raise CloudinaryException, "Server returned unexpected status code - #{response.code} - #{response.body}" if ![200,400,401,403,404,500].include?(response.code)
211
215
  begin
@@ -107,6 +107,8 @@ class Cloudinary::Utils
107
107
  shorten = config_option_consume(options, :shorten)
108
108
  force_remote = options.delete(:force_remote)
109
109
  cdn_subdomain = config_option_consume(options, :cdn_subdomain)
110
+ sign_url = config_option_consume(options, :sign_url)
111
+ secret = config_option_consume(options, :api_secret)
110
112
 
111
113
  original_source = source
112
114
  return original_source if source.blank?
@@ -165,9 +167,13 @@ class Cloudinary::Utils
165
167
  type = nil
166
168
  end
167
169
  version ||= 1 if source.include?("/") and !source.match(/^v[0-9]+/) and !source.match(/^https?:\//)
168
- source = prefix + "/" + [resource_type,
169
- type, transformation, version ? "v#{version}" : nil,
170
- source].reject(&:blank?).join("/").gsub(%r(([^:])//), '\1/')
170
+
171
+ rest = [transformation, version ? "v#{version}" : nil, source].reject(&:blank?).join("/").gsub(%r(([^:])//), '\1/')
172
+ if sign_url
173
+ rest = 's--' + Base64.urlsafe_encode64(Digest::SHA1.digest(rest + secret))[0,8] + '--/' + rest
174
+ end
175
+
176
+ source = prefix + "/" + [resource_type, type, rest].reject(&:blank?).join("/").gsub(%r(([^:])//), '\1/')
171
177
  end
172
178
 
173
179
  def self.cloudinary_api_url(action = 'upload', options = {})
@@ -272,6 +278,18 @@ class Cloudinary::Utils
272
278
  else [array]
273
279
  end
274
280
  end
281
+
282
+ def self.encode_hash(hash)
283
+ case hash
284
+ when Hash then hash.map{|k,v| "#{k}=#{v}"}.join("|")
285
+ when nil then ""
286
+ else hash
287
+ end
288
+ end
289
+
290
+ def self.encode_double_array(array)
291
+ build_array(array).map{|a| build_array(a).join(",")}.join("|")
292
+ end
275
293
 
276
294
  IMAGE_FORMATS = %w(bmp png tif tiff jpg jpeg gif pdf ico eps jpc jp2 psd)
277
295
 
@@ -1,4 +1,4 @@
1
1
  # Copyright Cloudinary
2
2
  module Cloudinary
3
- VERSION = "1.0.66"
3
+ VERSION = "1.0.67"
4
4
  end
data/spec/api_spec.rb CHANGED
@@ -8,8 +8,8 @@ describe Cloudinary::Api do
8
8
  @api = Cloudinary::Api
9
9
  Cloudinary::Uploader.destroy("api_test")
10
10
  Cloudinary::Uploader.destroy("api_test2")
11
- Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test", :tags=>"api_test_tag", :eager=>[:width=>100,:crop=>:scale])
12
- Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test2", :tags=>"api_test_tag", :eager=>[:width=>100,:crop=>:scale])
11
+ Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test", :tags=>"api_test_tag", :context => "key=value", :eager=>[:width=>100,:crop=>:scale])
12
+ Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test2", :tags=>"api_test_tag", :context => "key=value", :eager=>[:width=>100,:crop=>:scale])
13
13
  @api.delete_transformation("api_test_transformation") rescue nil
14
14
  @api.delete_transformation("api_test_transformation2") rescue nil
15
15
  @api.delete_transformation("api_test_transformation3") rescue nil
@@ -44,13 +44,25 @@ describe Cloudinary::Api do
44
44
  end
45
45
 
46
46
  it "should allow listing resources by prefix" do
47
- public_ids = @api.resources(:type=>"upload", :prefix=>"api_test")["resources"].map{|resource| resource["public_id"]}
48
- public_ids.should include("api_test", "api_test2")
47
+ resources = @api.resources(:type=>"upload", :prefix=>"api_test", :tags => true, :context => true)["resources"]
48
+ resources.map{|resource| resource["public_id"]}.should include("api_test", "api_test2")
49
+ resources.map{|resource| resource["tags"]}.should include(["api_test_tag"])
50
+ resources.map{|resource| resource["context"]}.should include({"custom" => {"key" => "value"}})
49
51
  end
50
52
 
51
53
  it "should allow listing resources by tag" do
52
- resource = @api.resources_by_tag("api_test_tag")["resources"].find{|resource| resource["public_id"] == "api_test"}
53
- resource.should_not be_blank
54
+ resources = @api.resources_by_tag("api_test_tag", :tags => true, :context => true)["resources"]
55
+ resources.find{|resource| resource["public_id"] == "api_test"}.should_not be_blank
56
+ resources.map{|resource| resource["tags"]}.should include(["api_test_tag"])
57
+ resources.map{|resource| resource["context"]}.should include({"custom" => {"key" => "value"}})
58
+ end
59
+
60
+ it "should allow listing resources by public ids" do
61
+ resources = @api.resources_by_ids(["api_test", "api_test2"], :tags => true, :context => true)["resources"]
62
+ resources.length.should == 2
63
+ resources.find{|resource| resource["public_id"] == "api_test"}.should_not be_blank
64
+ resources.map{|resource| resource["tags"]}.should include(["api_test_tag"])
65
+ resources.map{|resource| resource["context"]}.should include({"custom" => {"key" => "value"}})
54
66
  end
55
67
 
56
68
  it "should allow get resource metadata" do
@@ -75,4 +75,36 @@ describe Cloudinary::Uploader do
75
75
  result = Cloudinary::Uploader.upload("spec/logo.png", use_filename: true, unique_filename: false)
76
76
  result["public_id"].should == "logo"
77
77
  end
78
+
79
+ it "should allow whitelisted formats if allowed_formats", :allowed=>true do
80
+ result = Cloudinary::Uploader.upload("spec/logo.png", allowed_formats: ["png"])
81
+ result["format"].should == "png"
82
+ end
83
+
84
+ it "should prevent non whitelisted formats from being uploaded if allowed_formats is specified", :allowed=>true do
85
+ lambda{Cloudinary::Uploader.upload("spec/logo.png", allowed_formats: ["jpg"])}.should raise_error
86
+ end
87
+
88
+ it "should allow non whitelisted formats if type is specified and convert to that type", :allowed=>true do
89
+ result = Cloudinary::Uploader.upload("spec/logo.png", allowed_formats: ["jpg"], format: "jpg")
90
+ result["format"].should == "jpg"
91
+ end
92
+
93
+ it "should allow sending face coordinates" do
94
+ coordinates = [[120, 30, 109, 150], [121, 31, 110, 151]]
95
+ result = Cloudinary::Uploader.upload("spec/logo.png", {face_coordinates: coordinates, faces: true})
96
+ result["faces"].should == coordinates
97
+
98
+ different_coordinates = [[122, 32, 111, 152]]
99
+ Cloudinary::Uploader.explicit(result["public_id"], {face_coordinates: different_coordinates, faces: true, type: "upload"})
100
+ info = Cloudinary::Api.resource(result["public_id"], {faces: true})
101
+ info["faces"].should == different_coordinates
102
+ end
103
+
104
+ it "should allow sending context" do
105
+ context = {"caption" => "some caption", "alt" => "alternative"}
106
+ result = Cloudinary::Uploader.upload("spec/logo.png", {:context => context})
107
+ info = Cloudinary::Api.resource(result["public_id"], {:context => true})
108
+ info["context"].should == {"custom" => context}
109
+ end
78
110
  end
data/spec/utils_spec.rb CHANGED
@@ -12,7 +12,7 @@ describe Cloudinary::Utils do
12
12
  config.cname = nil
13
13
  config.cdn_subdomain = false
14
14
  config.api_key = "1234"
15
- config.api_secret = "1234"
15
+ config.api_secret = "b"
16
16
  end
17
17
  end
18
18
 
@@ -439,4 +439,25 @@ describe Cloudinary::Utils do
439
439
  Cloudinary::Utils.cloudinary_url(source).should == "http://res.cloudinary.com/test123/image/upload/#{target}"
440
440
  end
441
441
  end
442
+
443
+ it "should correctly sign URLs", :signed => true do
444
+ options = {version: 1234, transformation: {crop: "crop", width: 10, height: 20}, sign_url: true}
445
+ expected = "http://res.cloudinary.com/test123/image/upload/s--MaRXzoEC--/c_crop,h_20,w_10/v1234/image.jpg"
446
+ actual = Cloudinary::Utils.cloudinary_url("image.jpg", options)
447
+ actual.should == expected
448
+
449
+ options = {version: 1234, sign_url: true}
450
+ expected = "http://res.cloudinary.com/test123/image/upload/s--ZlgFLQcO--/v1234/image.jpg"
451
+ actual = Cloudinary::Utils.cloudinary_url("image.jpg", options)
452
+ actual.should == expected
453
+
454
+ options = {transformation: {crop: "crop", width: 10, height: 20}, sign_url: true}
455
+ expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"
456
+ actual = Cloudinary::Utils.cloudinary_url("image.jpg", options)
457
+ actual.should == expected
458
+
459
+ expected = "http://res.cloudinary.com/test123/image/fetch/s--_GAUclyB--/v1234/http://google.com/path/to/image.png"
460
+ actual = Cloudinary::Utils.cloudinary_url("http://google.com/path/to/image.png", type: "fetch", version: 1234, sign_url: true)
461
+ actual.should == expected
462
+ end
442
463
  end
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudinary
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.66
4
+ version: 1.0.67
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Nadav Soferman
@@ -10,46 +11,52 @@ authors:
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2013-11-14 00:00:00.000000000 Z
14
+ date: 2014-01-09 00:00:00.000000000 Z
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
17
+ name: rest-client
16
18
  requirement: !ruby/object:Gem::Requirement
19
+ none: false
17
20
  requirements:
18
21
  - - ! '>='
19
22
  - !ruby/object:Gem::Version
20
23
  version: '0'
21
24
  type: :runtime
22
25
  prerelease: false
23
- name: rest-client
24
26
  version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
25
28
  requirements:
26
29
  - - ! '>='
27
30
  - !ruby/object:Gem::Version
28
31
  version: '0'
29
32
  - !ruby/object:Gem::Dependency
33
+ name: aws_cf_signer
30
34
  requirement: !ruby/object:Gem::Requirement
35
+ none: false
31
36
  requirements:
32
37
  - - ! '>='
33
38
  - !ruby/object:Gem::Version
34
39
  version: '0'
35
40
  type: :runtime
36
41
  prerelease: false
37
- name: aws_cf_signer
38
42
  version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
39
44
  requirements:
40
45
  - - ! '>='
41
46
  - !ruby/object:Gem::Version
42
47
  version: '0'
43
48
  - !ruby/object:Gem::Dependency
49
+ name: rspec
44
50
  requirement: !ruby/object:Gem::Requirement
51
+ none: false
45
52
  requirements:
46
53
  - - ! '>='
47
54
  - !ruby/object:Gem::Version
48
55
  version: '0'
49
56
  type: :development
50
57
  prerelease: false
51
- name: rspec
52
58
  version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
53
60
  requirements:
54
61
  - - ! '>='
55
62
  - !ruby/object:Gem::Version
@@ -114,26 +121,27 @@ files:
114
121
  homepage: http://cloudinary.com
115
122
  licenses:
116
123
  - MIT
117
- metadata: {}
118
124
  post_install_message:
119
125
  rdoc_options: []
120
126
  require_paths:
121
127
  - lib
122
128
  required_ruby_version: !ruby/object:Gem::Requirement
129
+ none: false
123
130
  requirements:
124
131
  - - ! '>='
125
132
  - !ruby/object:Gem::Version
126
133
  version: '0'
127
134
  required_rubygems_version: !ruby/object:Gem::Requirement
135
+ none: false
128
136
  requirements:
129
137
  - - ! '>='
130
138
  - !ruby/object:Gem::Version
131
139
  version: '0'
132
140
  requirements: []
133
141
  rubyforge_project: cloudinary
134
- rubygems_version: 2.0.6
142
+ rubygems_version: 1.8.24
135
143
  signing_key:
136
- specification_version: 4
144
+ specification_version: 3
137
145
  summary: Client library for easily using the Cloudinary service
138
146
  test_files:
139
147
  - spec/api_spec.rb
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NjljOWM1ZTk5ODJmZDAyMzc3Y2Y0MGU0OGY0NTE5NTI5N2FkZjA3OA==
5
- data.tar.gz: !binary |-
6
- MmFiZjlmNzJjMDQwZDI4YzUzZmJkZjA0NjRjMzk0MzI2YzVhNDk1Yw==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- MWNkY2Y0MzQ1YjljZWM4OTYwZjkxNWY0ZGI1OWRmNjAwOWZlZjRkZTgxOGFm
10
- NmI3YjVhMDJlM2Y2MGJmYjk0NmFiNjVkMmI2NzkxNjM3NmViNTdkM2ZhNDc2
11
- MTE5MzQ1MjM0ZTY5OGJiMmU0NDliYzBhNDI4N2I0YTY2ZDNmMTM=
12
- data.tar.gz: !binary |-
13
- ZTk2ZGUwMTA5MjYyZmFkMWEzZmIzMTQ4NTQ5MjdhMzliMjk5YmQzNzNhZDhh
14
- NTBiNGM0MDcyZWVhOGMzODk5NDQ2ZTcxMWI3MTFlMDkzZDkyNTYzOGM5NDI5
15
- Yzc4Yjc1MmQ0ZGUxMTcwNDViNjI0ZmUyMTk4NTM2Njg0NDMyMjA=