cloudinary 1.0.66 → 1.0.67

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/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=