cloudinary 1.1.0 → 1.1.1

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/README.md CHANGED
@@ -22,23 +22,23 @@ To install the Cloudinary Ruby GEM, run:
22
22
 
23
23
  If you use Rails 3.x or higher, edit your `Gemfile`, add the following line and run `bundle install`
24
24
 
25
- $ gem 'cloudinary'
25
+ gem 'cloudinary'
26
26
 
27
27
  Or in Rails 2.x, edit your `environment.rb` and add:
28
28
 
29
- $ config.gem 'cloudinary'
29
+ config.gem 'cloudinary'
30
30
 
31
31
  If you would like to use our optional integration module of image uploads with ActiveRecord using `CarrierWave`, install CarrierWave GEM:
32
32
 
33
33
  Rails 3.x: edit your `Gemfile` and run `bundle install`:
34
34
 
35
- $ gem 'carrierwave'
36
- $ gem 'cloudinary'
35
+ gem 'carrierwave'
36
+ gem 'cloudinary'
37
37
 
38
38
  Rails 2.x environment.rb:
39
39
 
40
- $ config.gem 'carrierwave', :version => '~> 0.4.10'
41
- $ config.gem 'cloudinary'
40
+ config.gem 'carrierwave', :version => '~> 0.4.10'
41
+ config.gem 'cloudinary'
42
42
 
43
43
 
44
44
  *Note: The CarrierWave GEM should be loaded before the Cloudinary GEM.*
@@ -92,6 +92,18 @@ It contains settings for each of your deployment environments. You can always ov
92
92
 
93
93
  You can [download your customized cloudinary.yml](https://cloudinary.com/console/cloudinary.yml) configuration file using our Management Console.
94
94
 
95
+ Passing the parameters manually looks like this:
96
+
97
+ ``` ruby
98
+ auth = {
99
+ cloud_name: "somename",
100
+ api_key: "1234567890",
101
+ api_secret: "FooBarBaz123"
102
+ }
103
+
104
+ Cloudinary::Uploader.upload("my_picture.jpg", auth)
105
+ ```
106
+
95
107
 
96
108
  ### Embedding and transforming images
97
109
 
@@ -163,12 +175,15 @@ Cloudinary's Ruby GEM includes an optional plugin for [CarrierWave](https://gith
163
175
 
164
176
  We also published an interesting blog post about [Ruby on Rails image uploads with CarrierWave and Cloudinary](http://cloudinary.com/blog/ruby_on_rails_image_uploads_with_carrierwave_and_cloudinary).
165
177
 
178
+ ## Neo4j integration
179
+
180
+ Starting from version 1.1.1 Cloudinary's Ruby GEM supports the use of carrierwave with Neo4j.
181
+
166
182
  ### Samples
167
183
 
168
184
  You can find our simple and ready-to-use samples projects, along with documentation in the [samples folder](https://github.com/cloudinary/cloudinary_gem/tree/master/samples).
169
185
  Please consult with the [README file](https://github.com/cloudinary/cloudinary_gem/blob/master/samples/README.md), for usage and explanations.
170
186
 
171
-
172
187
  ## Additional resources ##########################################################
173
188
 
174
189
  Additional resources are available at:
@@ -193,3 +208,5 @@ Stay tuned for updates, tips and tutorials: [Blog](http://cloudinary.com/blog),
193
208
  ## License #######################################################################
194
209
 
195
210
  Released under the MIT license.
211
+
212
+
data/Rakefile CHANGED
@@ -1,4 +1,9 @@
1
1
  require 'bundler'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require 'rest_client'
5
+ require 'json'
6
+ require 'rubygems/package'
2
7
 
3
8
  require 'rspec/core/rake_task'
4
9
  RSpec::Core::RakeTask.new(:spec)
@@ -6,24 +11,45 @@ task :default => :spec
6
11
 
7
12
  Bundler::GemHelper.install_tasks
8
13
 
9
- task :fetch_assets do
10
- system "/bin/rm -rf vendor/assets; mkdir -p vendor/assets; cd vendor/assets; curl -L https://github.com/cloudinary/cloudinary_js/tarball/master | tar zxvf - --strip=1"
11
- system "mkdir -p vendor/assets/javascripts; mv vendor/assets/js vendor/assets/javascripts/cloudinary"
12
- File.open("vendor/assets/javascripts/cloudinary/index.js", "w") do
13
- |f|
14
- f.puts "//= require ./jquery.ui.widget.js"
15
- f.puts "//= require ./jquery.iframe-transport.js"
16
- f.puts "//= require ./jquery.fileupload.js"
17
- f.puts "//= require ./jquery.cloudinary.js"
18
- end
19
- File.open("vendor/assets/javascripts/cloudinary/processing.js", "w") do
20
- |f|
21
- f.puts "//= require ./canvas-to-blob.min.js"
22
- f.puts "//= require ./load-image.min.js"
23
- f.puts "//= require ./jquery.fileupload-process.js"
24
- f.puts "//= require ./jquery.fileupload-image.js"
25
- f.puts "//= require ./jquery.fileupload-validate.js"
14
+ namespace :cloudinary do
15
+ desc "Fetch the latest JavaScript library files and create the JavaScript index files"
16
+ task :fetch_assets do
17
+ index_files = %w[jquery.ui.widget.js jquery.iframe-transport.js jquery.fileupload.js jquery.cloudinary.js]
18
+ processing_files = %w[canvas-to-blob.min.js load-image.all.min.js jquery.fileupload-process.js jquery.fileupload-image.js jquery.fileupload-validate.js]
19
+ files = index_files + processing_files
20
+
21
+ release = JSON(RestClient.get("https://api.github.com/repos/cloudinary/cloudinary_js/releases/latest"))
22
+
23
+ FileUtils.rm_rf 'vendor/assets'
24
+ html_folder = 'vendor/assets/html'
25
+ FileUtils.mkdir_p html_folder
26
+ js_folder = 'vendor/assets/javascripts/cloudinary'
27
+ FileUtils.mkdir_p js_folder
28
+
29
+ puts "Fetching cloudinary_js version #{release["tag_name"]}\n\n"
30
+ sio = StringIO.new(RestClient.get(release["tarball_url"]).body)
31
+ file =Zlib::GzipReader.new(sio)
32
+ tar = Gem::Package::TarReader.new(file)
33
+ tar.each_entry do |entry|
34
+ name = File.basename(entry.full_name)
35
+ if files.include? name
36
+ js_full_name = File.join(js_folder, name)
37
+ puts "Adding #{js_full_name}"
38
+ File.write js_full_name, entry.read
39
+ elsif name == 'cloudinary_cors.html'
40
+ html_full_name = File.join(html_folder, name)
41
+ puts "Adding #{html_full_name}"
42
+ File.write html_full_name, entry.read
43
+ end
44
+ end
45
+ puts "Creating 'index.js' and 'processing.js' files"
46
+ File.open("vendor/assets/javascripts/cloudinary/index.js", "w") do |f|
47
+ index_files.each { |name| f.puts "//= require ./#{name}" }
48
+ end
49
+ File.open("vendor/assets/javascripts/cloudinary/processing.js", "w") do |f|
50
+ processing_files.each { |name| f.puts "//= require ./#{name}" }
51
+ end
26
52
  end
27
- end
28
53
 
29
- task :build=>:fetch_assets
54
+ end
55
+ task :build => "cloudinary:fetch_assets"
@@ -68,9 +68,10 @@ class Cloudinary::CarrierWave::Storage < ::CarrierWave::Storage::Abstract
68
68
  column = uploader.model.send(:_mounter, uploader.mounted_as).send(:serialization_column)
69
69
  if defined?(ActiveRecord::Base) && uploader.model.is_a?(ActiveRecord::Base)
70
70
  primary_key = model_class.primary_key.to_sym
71
- if Rails.version >= "3.0"
71
+ if defined?(::ActiveRecord::VERSION::MAJOR) && ::ActiveRecord::VERSION::MAJOR > 2
72
72
  model_class.where(primary_key=>uploader.model.send(primary_key)).update_all(column=>name)
73
73
  else
74
+ # Removed since active record version 3.0.0
74
75
  model_class.update_all({column=>name}, {primary_key=>uploader.model.send(primary_key)})
75
76
  end
76
77
  uploader.model.send :write_attribute, column, name
@@ -83,6 +84,8 @@ class Cloudinary::CarrierWave::Storage < ::CarrierWave::Storage::Abstract
83
84
  else
84
85
  uploader.model.set(column, name)
85
86
  end
87
+ elsif defined?(Neo4j::VERSION) && Neo4j::VERSION.split(".").first.to_i >= 5
88
+ uploader.model.set(column, name)
86
89
  elsif defined?(Sequel::Model) && uploader.model.is_a?(Sequel::Model)
87
90
  # Sequel support
88
91
  uploader.model.this.update(column => name)
@@ -250,7 +250,7 @@ module CloudinaryHelper
250
250
  if !method_defined?(:image_tag)
251
251
  include ActionView::Helpers::AssetTagHelper
252
252
  end
253
- if defined?(Rails::version) && !Rails.version.start_with?('2') && Cloudinary.config.enhance_image_tag
253
+ if defined?(::Rails::VERSION::MAJOR) && ::Rails::VERSION::MAJOR > 2 && Cloudinary.config.enhance_image_tag
254
254
  alias_method_chain :image_tag, :cloudinary # defines image_tag_without_cloudinary
255
255
  alias_method_chain :image_path, :cloudinary # defines image_path_without_cloudinary
256
256
  else
@@ -316,7 +316,7 @@ if defined? ActionView::Helpers::AssetUrlHelper
316
316
  end
317
317
  end
318
318
 
319
- if defined?(Rails::version) && Rails.version.start_with?('2')
319
+ if defined?(::Rails::VERSION::MAJOR) && ::Rails::VERSION::MAJOR == 2
320
320
  ActionView::Base.send :include, ActionView::Helpers::AssetTagHelper
321
321
  ActionView::Base.send :include, CloudinaryHelper
322
322
  end
@@ -14,11 +14,9 @@ class Cloudinary::Utils
14
14
  if options.is_a?(Array)
15
15
  return options.map{|base_transformation| generate_transformation_string(base_transformation.clone)}.join("/")
16
16
  end
17
- # Symbolize keys
18
- options.keys.each do |key|
19
- options[(key.to_sym rescue key)] = options.delete(key)
20
- end
21
-
17
+
18
+ symbolize_keys!(options)
19
+
22
20
  responsive_width = config_option_consume(options, :responsive_width)
23
21
  size = options.delete(:size)
24
22
  options[:width], options[:height] = size.split("x") if size
@@ -70,6 +68,9 @@ class Cloudinary::Utils
70
68
  options[:start_offset], options[:end_offset] = split_range options.delete(:offset)
71
69
  end
72
70
 
71
+ overlay = process_layer(options.delete(:overlay))
72
+ underlay = process_layer(options.delete(:underlay))
73
+
73
74
  params = {
74
75
  :a => angle,
75
76
  :b => background,
@@ -80,7 +81,9 @@ class Cloudinary::Utils
80
81
  :e => effect,
81
82
  :fl => flags,
82
83
  :h => height,
84
+ :l => overlay,
83
85
  :t => named_transformation,
86
+ :u => underlay,
84
87
  :w => width
85
88
  }
86
89
  {
@@ -94,7 +97,6 @@ class Cloudinary::Utils
94
97
  :eo => :end_offset,
95
98
  :f => :fetch_format,
96
99
  :g => :gravity,
97
- :l => :overlay,
98
100
  :o => :opacity,
99
101
  :p => :prefix,
100
102
  :pg => :page,
@@ -102,7 +104,6 @@ class Cloudinary::Utils
102
104
  :r => :radius,
103
105
  :af => :audio_frequency,
104
106
  :so => :start_offset,
105
- :u => :underlay,
106
107
  :vc => :video_codec,
107
108
  :vs => :video_sampling,
108
109
  :x => :x,
@@ -137,6 +138,84 @@ class Cloudinary::Utils
137
138
  transformations.reject(&:blank?).join("/")
138
139
  end
139
140
 
141
+ # Parse layer options
142
+ # @return [string] layer transformation string
143
+ # @private
144
+ def self.process_layer(layer)
145
+ if layer.is_a? Hash
146
+ layer = symbolize_keys layer
147
+ public_id = layer[:public_id]
148
+ format = layer[:format]
149
+ resource_type = layer[:resource_type] || "image"
150
+ type = layer[:type] || "upload"
151
+ text = layer[:text]
152
+ text_style = nil
153
+ components = []
154
+
155
+ unless public_id.blank?
156
+ public_id = public_id.gsub("/", ":")
157
+ public_id = "#{public_id}.#{format}" unless format.nil?
158
+ end
159
+
160
+ if text.blank? && resource_type != "text"
161
+ if public_id.blank?
162
+ raise(CloudinaryException, "Must supply public_id for resource_type layer_parameter")
163
+ end
164
+ if resource_type == "subtitles"
165
+ text_style = text_style(layer)
166
+ end
167
+
168
+ else
169
+ resource_type = "text"
170
+ type = nil
171
+ # // type is ignored for text layers
172
+ text_style = text_style(layer)
173
+ unless text.blank?
174
+ unless public_id.blank? ^ text_style.blank?
175
+ raise(CloudinaryException, "Must supply either style parameters or a public_id when providing text parameter in a text overlay/underlay")
176
+ end
177
+ text = smart_escape smart_escape(text, %r"([,/])")
178
+
179
+ end
180
+ end
181
+ components.push(resource_type) if resource_type != "image"
182
+ components.push(type) if type != "upload"
183
+ components.push(text_style)
184
+ components.push(public_id)
185
+ components.push(text)
186
+ layer = components.reject(&:blank?).join(":")
187
+ end
188
+ layer
189
+ end
190
+ private_class_method :process_layer
191
+
192
+ LAYER_KEYWORD_PARAMS ={
193
+ :font_weight => "normal",
194
+ :font_style => "normal",
195
+ :text_decoration => "none",
196
+ :text_align => nil,
197
+ :stroke => "none"
198
+ }
199
+
200
+ def self.text_style(layer)
201
+ font_family = layer[:font_family]
202
+ font_size = layer[:font_size]
203
+ keywords = []
204
+ LAYER_KEYWORD_PARAMS.each do |attr, default_value|
205
+ attr_value = layer[attr] || default_value
206
+ keywords.push(attr_value) unless attr_value == default_value
207
+ end
208
+ letter_spacing = layer[:letter_spacing]
209
+ keywords.push("letter_spacing_#{letter_spacing}") unless letter_spacing.blank?
210
+ if !font_size.blank? || !font_family.blank? || !keywords.empty?
211
+ raise(CloudinaryException, "Must supply font_family for text in overlay/underlay") if font_family.blank?
212
+ raise(CloudinaryException, "Must supply font_size for text in overlay/underlay") if font_size.blank?
213
+ keywords.unshift(font_size)
214
+ keywords.unshift(font_family)
215
+ keywords.reject(&:blank?).join("_")
216
+ end
217
+ end
218
+
140
219
  def self.api_string_to_sign(params_to_sign)
141
220
  params_to_sign.map{|k,v| [k.to_s, v.is_a?(Array) ? v.join(",") : v]}.reject{|k,v| v.nil? || v == ""}.sort_by(&:first).map{|k,v| "#{k}=#{v}"}.join("&")
142
221
  end
@@ -151,7 +230,7 @@ class Cloudinary::Utils
151
230
 
152
231
  type = options.delete(:type)
153
232
 
154
- options[:fetch_format] ||= options.delete(:format) if type == :fetch
233
+ options[:fetch_format] ||= options.delete(:format) if type.to_s == "fetch"
155
234
  transformation = self.generate_transformation_string(options)
156
235
 
157
236
  resource_type = options.delete(:resource_type)
@@ -184,10 +263,11 @@ class Cloudinary::Utils
184
263
  type ||= source.storage_type
185
264
  source = format.blank? ? source.filename : source.full_public_id
186
265
  end
266
+ type = type.to_s unless type.nil?
187
267
  resource_type ||= "image"
188
268
  source = source.to_s
189
269
  if !force_remote
190
- return original_source if (type.nil? || type == :asset) && source.match(%r(^https?:/)i)
270
+ return original_source if (type.nil? || type == "asset") && source.match(%r(^https?:/)i)
191
271
  if source.start_with?("/")
192
272
  if source.start_with?("/images/")
193
273
  source = source.sub(%r(/images/), '')
@@ -196,11 +276,11 @@ class Cloudinary::Utils
196
276
  end
197
277
  end
198
278
  @metadata ||= defined?(Cloudinary::Static) ? Cloudinary::Static.metadata : {}
199
- if type == :asset && @metadata["images/#{source}"]
279
+ if type == "asset" && @metadata["images/#{source}"]
200
280
  return original_source if !Cloudinary.config.static_image_support
201
281
  source = @metadata["images/#{source}"]["public_id"]
202
282
  source += File.extname(original_source) if !format
203
- elsif type == :asset
283
+ elsif type == "asset"
204
284
  return original_source # requested asset, but no metadata - probably local file. return.
205
285
  end
206
286
  end
@@ -214,11 +294,16 @@ class Cloudinary::Utils
214
294
  transformation = transformation.gsub(%r(([^:])//), '\1/')
215
295
  if sign_url
216
296
  to_sign = [transformation, sign_version && version, source_to_sign].reject(&:blank?).join("/")
297
+ i = 0
298
+ while to_sign != CGI.unescape(to_sign) && i <10
299
+ to_sign = CGI.unescape(to_sign)
300
+ i = i + 1
301
+ end
217
302
  signature = 's--' + Base64.urlsafe_encode64(Digest::SHA1.digest(to_sign + secret))[0,8] + '--'
218
303
  end
219
304
 
220
305
  prefix = unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
221
- source = [prefix, resource_type, type, signature, transformation, version, source].reject(&:blank?).join("/")
306
+ source = [prefix, resource_type, type, signature, transformation, version, source].reject(&:blank?).join("/")
222
307
  end
223
308
 
224
309
  def self.finalize_source(source, format, url_suffix)
@@ -369,8 +454,8 @@ class Cloudinary::Utils
369
454
  end
370
455
 
371
456
  # Based on CGI::unescape. In addition does not escape / :
372
- def self.smart_escape(string)
373
- string.gsub(/([^a-zA-Z0-9_.\-\/:]+)/) do
457
+ def self.smart_escape(string, unsafe = /([^a-zA-Z0-9_.\-\/:]+)/)
458
+ string.gsub(unsafe) do
374
459
  '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
375
460
  end
376
461
  end
@@ -468,18 +553,60 @@ class Cloudinary::Utils
468
553
  value.nil? || value == "" || value == []
469
554
  end
470
555
 
556
+ def self.symbolize_keys(h)
557
+ new_h = Hash.new
558
+ if (h.respond_to? :keys)
559
+ h.keys.each do |key|
560
+ new_h[(key.to_sym rescue key)] = h[key]
561
+ end
562
+ end
563
+ new_h
564
+ end
565
+
566
+
567
+ def self.symbolize_keys!(h)
568
+ if (h.respond_to? :keys) && (h.respond_to? :delete)
569
+ h.keys.each do |key|
570
+ value = h.delete(key)
571
+ h[(key.to_sym rescue key)] = value
572
+ end
573
+ end
574
+ h
575
+ end
576
+
577
+
578
+ def self.deep_symbolize_keys(object)
579
+ case object
580
+ when Hash
581
+ result = {}
582
+ object.each do |key, value|
583
+ key = key.to_sym rescue key
584
+ result[key] = deep_symbolize_keys(value)
585
+ end
586
+ result
587
+ when Array
588
+ object.map{|e| deep_symbolize_keys(e)}
589
+ else
590
+ object
591
+ end
592
+ end
593
+
471
594
  private
595
+
472
596
  def self.number_pattern
473
597
  "([0-9]*)\\.([0-9]+)|([0-9]+)"
474
598
  end
599
+ private_class_method :number_pattern
475
600
 
476
601
  def self.offset_any_pattern
477
602
  "(#{number_pattern})([%pP])?"
478
603
  end
604
+ private_class_method :offset_any_pattern
479
605
 
480
606
  def self.offset_any_pattern_re
481
607
  /((([0-9]*)\.([0-9]+)|([0-9]+))([%pP])?)\.\.((([0-9]*)\.([0-9]+)|([0-9]+))([%pP])?)/
482
608
  end
609
+ private_class_method :offset_any_pattern_re
483
610
 
484
611
  # Split a range into the start and end values
485
612
  def self.split_range(range) # :nodoc:
@@ -494,10 +621,12 @@ class Cloudinary::Utils
494
621
  nil
495
622
  end
496
623
  end
624
+ private_class_method :split_range
497
625
 
498
626
  # Normalize an offset value
499
627
  # @param [String] value a decimal value which may have a 'p' or '%' postfix. E.g. '35%', '0.4p'
500
628
  # @return [Object|String] a normalized String of the input value if possible otherwise the value itself
629
+ # @private
501
630
  def self.norm_range_value(value) # :nodoc:
502
631
  offset = /^#{offset_any_pattern}$/.match( value.to_s)
503
632
  if offset
@@ -506,6 +635,7 @@ class Cloudinary::Utils
506
635
  end
507
636
  value
508
637
  end
638
+ private_class_method :norm_range_value
509
639
 
510
640
  # A video codec parameter can be either a String or a Hash.
511
641
  #
@@ -514,6 +644,7 @@ class Cloudinary::Utils
514
644
  # @return [String] <code><codec> : <profile> : [<level>]]</code> if a Hash was provided
515
645
  # or the param if a String was provided.
516
646
  # Returns NIL if param is not a Hash or String
647
+ # @private
517
648
  def self.process_video_params(param)
518
649
  case param
519
650
  when Hash
@@ -534,21 +665,6 @@ class Cloudinary::Utils
534
665
  nil
535
666
  end
536
667
  end
537
-
538
- def self.deep_symbolize_keys(object)
539
- case object
540
- when Hash
541
- result = {}
542
- object.each do |key, value|
543
- key = key.to_sym rescue key
544
- result[key] = deep_symbolize_keys(value)
545
- end
546
- result
547
- when Array
548
- object.map{|e| deep_symbolize_keys(e)}
549
- else
550
- object
551
- end
552
- end
668
+ private_class_method :process_video_params
553
669
 
554
670
  end