cloudinary 1.0.1 → 1.0.2

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/lib/cloudinary.rb CHANGED
@@ -4,62 +4,13 @@ require "cloudinary/version"
4
4
  require "cloudinary/utils"
5
5
  require "cloudinary/uploader"
6
6
  require "cloudinary/downloader"
7
- require "cloudinary/migrator"
8
7
  require "cloudinary/blob"
8
+ require "cloudinary/static"
9
9
  require 'active_support'
10
- if defined?(::CarrierWave)
11
- require "cloudinary/carrier_wave"
12
- end
13
-
14
- if defined?(::ActionView::Base)
15
- require "cloudinary/helper"
16
- end
17
-
18
- if !nil.respond_to?(:blank?)
19
- class Object
20
- def blank?
21
- respond_to?(:empty?) ? empty? : !self
22
- end
23
- end
24
-
25
- class NilClass #:nodoc:
26
- def blank?
27
- true
28
- end
29
- end
30
-
31
- class FalseClass #:nodoc:
32
- def blank?
33
- true
34
- end
35
- end
36
-
37
- class TrueClass #:nodoc:
38
- def blank?
39
- false
40
- end
41
- end
42
-
43
- class Array #:nodoc:
44
- alias_method :blank?, :empty?
45
- end
46
-
47
- class Hash #:nodoc:
48
- alias_method :blank?, :empty?
49
- end
50
-
51
- class String #:nodoc:
52
- def blank?
53
- self !~ /\S/
54
- end
55
- end
56
-
57
- class Numeric #:nodoc:
58
- def blank?
59
- false
60
- end
61
- end
62
- end
10
+ require "cloudinary/missing"
11
+ require "cloudinary/carrier_wave" if defined?(::CarrierWave)
12
+ require "cloudinary/helper" if defined?(::ActionView::Base)
13
+ require "cloudinary/railtie" if defined?(Rails)
63
14
 
64
15
  module Cloudinary
65
16
  @@config = nil
@@ -1,6 +1,10 @@
1
1
  # Copyright Cloudinary
2
2
 
3
- module CloudinaryHelper
3
+ module CloudinaryHelper
4
+ include ActionView::Helpers::AssetTagHelper
5
+ alias :original_image_tag :image_tag
6
+ alias :original_image_path :image_path
7
+
4
8
  # Examples
5
9
  # cl_image_tag "israel.png", :width=>100, :height=>100, :alt=>"hello" # W/H are not sent to cloudinary
6
10
  # cl_image_tag "israel.png", :width=>100, :height=>100, :alt=>"hello", :crop=>:fit # W/H are sent to cloudinary
@@ -9,8 +13,35 @@ module CloudinaryHelper
9
13
  source = cloudinary_url(source, options)
10
14
  options[:width] = options.delete(:html_width) if options.include?(:html_width)
11
15
  options[:height] = options.delete(:html_height) if options.include?(:html_height)
16
+ options[:size] = options.delete(:html_size) if options.include?(:html_size)
12
17
 
13
- image_tag(source, options)
18
+ original_image_tag(source, options)
19
+ end
20
+
21
+ def cl_image_path(source, options = {})
22
+ options = options.clone
23
+ url = cloudinary_url(source, options)
24
+ original_image_path(url, options)
25
+ end
26
+
27
+ def image_tag(*args)
28
+ if Cloudinary.config.enhance_image_tag
29
+ cl_image_tag(*args)
30
+ else
31
+ original_image_tag(*args)
32
+ end
33
+ end
34
+
35
+ def image_path(*args)
36
+ if Cloudinary.config.enhance_image_tag
37
+ cl_image_path(*args)
38
+ else
39
+ original_image_path(*args)
40
+ end
41
+ end
42
+
43
+ def fetch_image_tag(profile, options = {})
44
+ cl_image_tag(profile, {:type=>:fetch}.merge(options))
14
45
  end
15
46
 
16
47
  def facebook_profile_image_tag(profile, options = {})
@@ -51,3 +82,17 @@ module CloudinaryHelper
51
82
  end
52
83
 
53
84
  ActionView::Base.send :include, CloudinaryHelper
85
+
86
+ if defined?(Sass::Rails)
87
+ class Sass::Rails::Resolver
88
+ alias :original_image_path :image_path
89
+ def image_path(img)
90
+ if Cloudinary.config.enhance_image_tag
91
+ Cloudinary::Utils.cloudinary_url(img, :type=>:asset)
92
+ else
93
+ original_image_path(img)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
@@ -37,6 +37,7 @@ class Cloudinary::Migrator
37
37
  @debug = options[:debug] || false
38
38
  @ignore_duplicates = options[:ignore_duplicates]
39
39
  @threads = [options[:threads] || 10, 100].min
40
+ @threads = 1 if RUBY_VERSION < "1.9"
40
41
  @extra_options = {:api_key=>options[:api_key], :api_secret=>options[:api_secret]}
41
42
  @delete_after_done = options[:delete_after_done] || options[:private_database]
42
43
  @max_processing = @threads * 10
@@ -0,0 +1,45 @@
1
+ if !nil.respond_to?(:blank?)
2
+ class Object
3
+ def blank?
4
+ respond_to?(:empty?) ? empty? : !self
5
+ end
6
+ end
7
+
8
+ class NilClass #:nodoc:
9
+ def blank?
10
+ true
11
+ end
12
+ end
13
+
14
+ class FalseClass #:nodoc:
15
+ def blank?
16
+ true
17
+ end
18
+ end
19
+
20
+ class TrueClass #:nodoc:
21
+ def blank?
22
+ false
23
+ end
24
+ end
25
+
26
+ class Array #:nodoc:
27
+ alias_method :blank?, :empty?
28
+ end
29
+
30
+ class Hash #:nodoc:
31
+ alias_method :blank?, :empty?
32
+ end
33
+
34
+ class String #:nodoc:
35
+ def blank?
36
+ self !~ /\S/
37
+ end
38
+ end
39
+
40
+ class Numeric #:nodoc:
41
+ def blank?
42
+ false
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ class Cloudinary::Railtie < Rails::Railtie
2
+ rake_tasks do
3
+ Dir[File.join(File.dirname(__FILE__),'../tasks/*.rake')].each { |f| load f }
4
+ end
5
+ end
@@ -0,0 +1,107 @@
1
+ require 'find'
2
+ class Cloudinary::Static
3
+ IGNORE_FILES = [".svn", "CVS", "RCS", ".git", ".hg", /^.htaccess/]
4
+ STATIC_IMAGE_DIRS = ["app/assets/images", "public/images"]
5
+ METADATA_FILE = ".cloudinary.static"
6
+ METADATA_TRASH_FILE = ".cloudinary.static.trash"
7
+
8
+ def self.discover
9
+ ignore_files = Cloudinary.config.ignore_files || IGNORE_FILES
10
+ relative_dirs = Cloudinary.config.statis_image_dirs || STATIC_IMAGE_DIRS
11
+ dirs = relative_dirs.map{|dir| Rails.root.join(dir)}.select(&:exist?)
12
+ dirs.each do
13
+ |dir|
14
+ dir.find do
15
+ |path|
16
+ file = path.basename.to_s
17
+ if IGNORE_FILES.any?{|pattern| pattern.is_a?(String) ? pattern == file : file.match(pattern)}
18
+ Find.prune
19
+ next
20
+ elsif path.directory?
21
+ next
22
+ else
23
+ relative_path = path.relative_path_from(Rails.root)
24
+ public_path = path.relative_path_from(dir.dirname)
25
+ yield(relative_path, public_path)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ UTC = ActiveSupport::TimeZone["UTC"]
32
+
33
+ def self.metadata_file_path
34
+ Rails.root.join(METADATA_FILE)
35
+ end
36
+
37
+ def self.metadata_trash_file_path
38
+ Rails.root.join(METADATA_TRASH_FILE)
39
+ end
40
+
41
+ def self.metadata(metadata_file = metadata_file_path, hash=true)
42
+ metadata = []
43
+ if File.exist?(metadata_file)
44
+ IO.foreach(metadata_file) do
45
+ |line|
46
+ line.strip!
47
+ next if line.blank?
48
+ path, public_id, upload_time, version, width, height = line.split("\t")
49
+ metadata << [path, {
50
+ "public_id" => public_id,
51
+ "upload_time" => UTC.at(upload_time.to_i),
52
+ "version" => version,
53
+ "width" => width.to_i,
54
+ "height" => height.to_i
55
+ }]
56
+ end
57
+ end
58
+ hash ? Hash[*metadata.flatten] : metadata
59
+ end
60
+
61
+ def self.sync(options={})
62
+ options = options.clone
63
+ delete_missing = options.delete(:delete_missing)
64
+ metadata = self.metadata
65
+ found_paths = Set.new
66
+ found_public_ids = Set.new
67
+ metadata_lines = []
68
+ self.discover do
69
+ |path, public_path|
70
+ next if found_paths.include?(path)
71
+ found_paths << path
72
+ data = Rails.root.join(path).read(:mode=>"rb")
73
+ ext = path.extname
74
+ format = ext[1..-1]
75
+ md5 = Digest::MD5.hexdigest(data)
76
+ public_id = "#{public_path.basename(ext)}-#{md5}"
77
+ found_public_ids << public_id
78
+ current_metadata = metadata.delete(public_path.to_s)
79
+ if current_metadata && current_metadata["public_id"] == public_id # Signature match
80
+ result = current_metadata
81
+ else
82
+ result = Cloudinary::Uploader.upload(Cloudinary::Blob.new(data, :original_filename=>path.to_s),
83
+ options.merge(:format=>format, :public_id=>public_id, :type=>:asset)
84
+ )
85
+ end
86
+ metadata_lines << [public_path, public_id, Time.now.to_i, result["version"], result["width"], result["height"]].join("\t")+"\n"
87
+ end
88
+ File.open(self.metadata_file_path, "w"){|f| f.print(metadata_lines.join)}
89
+ # Files no longer needed
90
+ trash = metadata.to_a + self.metadata(metadata_trash_file_path, false).reject{|public_path, info| found_public_ids.include?(info["public_id"])}
91
+
92
+ if delete_missing
93
+ trash.each do
94
+ |path, info|
95
+ Cloudinary::Uploader.destroy(info["public_id"], options.merge(:type=>:asset))
96
+ end
97
+ FileUtils.rm_f(self.metadata_trash_file_path)
98
+ else
99
+ # Add current removed file to the trash file.
100
+ metadata_lines = trash.map do
101
+ |public_path, info|
102
+ [public_path, info["public_id"], info["upload_time"].to_i, info["version"], info["width"], info["height"]].join("\t")+"\n"
103
+ end
104
+ File.open(self.metadata_trash_file_path, "w"){|f| f.print(metadata_lines.join)}
105
+ end
106
+ end
107
+ end
@@ -9,7 +9,9 @@ class Cloudinary::Uploader
9
9
  params = {:timestamp=>Time.now.to_i,
10
10
  :transformation => Cloudinary::Utils.generate_transformation_string(options),
11
11
  :public_id=> options[:public_id],
12
+ :callback=> options[:callback],
12
13
  :format=>options[:format],
14
+ :type=>options[:type],
13
15
  :tags=>options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(",")}.reject{|k,v| v.blank?}
14
16
  if options[:eager]
15
17
  params[:eager] = options[:eager].map do
@@ -27,6 +29,16 @@ class Cloudinary::Uploader
27
29
  [params, [:file]]
28
30
  end
29
31
  end
32
+
33
+ def self.destroy(public_id, options={})
34
+ call_api("destroy", options) do
35
+ {
36
+ :timestamp=>Time.now.to_i,
37
+ :type=>options[:type],
38
+ :public_id=> public_id
39
+ }
40
+ end
41
+ end
30
42
 
31
43
  def self.generate_sprite(tag, options={})
32
44
  version_store = options.delete(:version_store)
@@ -10,18 +10,22 @@ class Cloudinary::Utils
10
10
  height = options[:height]
11
11
  size = options.delete(:size)
12
12
  width, height = size.split("x") if size
13
- options.delete(:width) if width && width < 1
14
- options.delete(:height) if height && height < 1
13
+ options.delete(:width) if width && width.to_f < 1
14
+ options.delete(:height) if height && height.to_f < 1
15
15
 
16
16
  crop = options.delete(:crop)
17
17
  width=height=nil if crop.nil?
18
+
19
+ x = options.delete(:x)
20
+ y = options.delete(:y)
21
+ radius = options.delete(:radius)
18
22
 
19
23
  gravity = options.delete(:gravity)
20
24
  quality = options.delete(:quality)
21
25
  named_transformation = build_array(options.delete(:transformation)).join(".")
22
26
  prefix = options.delete(:prefix)
23
27
 
24
- params = {:w=>width, :h=>height, :t=>named_transformation, :c=>crop, :q=>quality, :g=>gravity, :p=>prefix}
28
+ params = {:w=>width, :h=>height, :t=>named_transformation, :c=>crop, :q=>quality, :g=>gravity, :p=>prefix, :x=>x, :y=>y, :radius=>radius}
25
29
  transformation = params.reject{|k,v| v.blank?}.map{|k,v| [k.to_s, v]}.sort_by(&:first).map{|k,v| "#{k}_#{v}"}.join(",")
26
30
  raw_transformation = options.delete(:raw_transformation)
27
31
  transformation = [transformation, raw_transformation].reject(&:blank?).join(",")
@@ -37,23 +41,37 @@ class Cloudinary::Utils
37
41
  def self.cloudinary_url(source, options = {})
38
42
  transformation = self.generate_transformation_string(options)
39
43
 
40
- type = options.delete(:type) || :upload
44
+ type = options.delete(:type)
41
45
  resource_type = options.delete(:resource_type) || "image"
42
46
  version = options.delete(:version)
43
-
44
47
  format = options.delete(:format)
45
- source = "#{source}.#{format}" if format
46
48
 
47
- # Configuration options
48
- # newsodrome.cloudinary.com, images.newsodrome.com, cloudinary.com/res/newsodrome, a9fj209daf.cloudfront.net
49
49
  cloud_name = options.delete(:cloud_name) || Cloudinary.config.cloud_name || raise("Must supply cloud_name in tag or in configuration")
50
+ secure = options.delete(:secure) || Cloudinary.config.secure
51
+ private_cdn = options.delete(:private_cdn) || Cloudinary.config.private_cdn
52
+ secure_distribution = options.delete(:secure_distribution) || Cloudinary.config.secure_distribution
53
+
54
+ return source if :type.nil? && source.match(%r(^https?:/)i)
55
+ if source.start_with?("/")
56
+ if source.start_with?("/images/")
57
+ source = source.sub(%r(/images/), '')
58
+ else
59
+ return source
60
+ end
61
+ end
62
+ type ||= :upload
63
+ @metadata ||= Cloudinary::Static.metadata
64
+ if @metadata["images/#{source}"]
65
+ return source if !Cloudinary.config.static_image_support
66
+ type = :asset
67
+ original_source = source
68
+ source = @metadata["images/#{source}"]["public_id"]
69
+ source += File.extname(original_source) if !format
70
+ end
50
71
 
51
72
  if cloud_name.start_with?("/")
52
73
  prefix = "/res" + cloud_name
53
74
  else
54
- secure = options.delete(:secure) || Cloudinary.config.secure
55
- private_cdn = options.delete(:private_cdn) || Cloudinary.config.private_cdn
56
- secure_distribution = options.delete(:secure_distribution) || Cloudinary.config.secure_distribution
57
75
  if secure && secure_distribution.nil?
58
76
  if private_cdn
59
77
  raise "secure_distribution not defined"
@@ -70,9 +88,26 @@ class Cloudinary::Utils
70
88
  prefix += "/#{cloud_name}" if !private_cdn
71
89
  end
72
90
 
91
+ source = "#{source}.#{format}" if format && type != :fetch
92
+ source = smart_escape(source) if [:fetch, :asset].include?(type)
73
93
  source = prefix + "/" + [resource_type,
74
94
  type, transformation, version ? "v#{version}" : nil,
75
- source].reject(&:blank?).join("/").gsub("//", "/")
95
+ source].reject(&:blank?).join("/").gsub(%r(([^:])//), '\1/')
96
+ end
97
+
98
+ def self.asset_file_name(path)
99
+ data = Rails.root.join(path).read(:mode=>"rb")
100
+ ext = path.extname
101
+ md5 = Digest::MD5.hexdigest(data)
102
+ public_id = "#{path.basename(ext)}-#{md5}"
103
+ "#{public_id}#{ext}"
104
+ end
105
+
106
+ # Based on CGI::unescape. In addition does not escape / :
107
+ def self.smart_escape(string)
108
+ string.gsub(/([^ a-zA-Z0-9_.-\/:]+)/) do
109
+ '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
110
+ end.tr(' ', '+')
76
111
  end
77
112
 
78
113
  def self.random_public_id
@@ -1,4 +1,4 @@
1
1
  # Copyright Cloudinary
2
2
  module Cloudinary
3
- VERSION = "1.0.1"
3
+ VERSION = "1.0.2"
4
4
  end
@@ -0,0 +1,6 @@
1
+ namespace :cloudinary do
2
+ desc "Sync static resources with cloudinary"
3
+ task :sync_static do
4
+ Cloudinary::Static.sync
5
+ end
6
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: cloudinary
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.0.1
5
+ version: 1.0.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Nadav Soferman
@@ -12,7 +12,7 @@ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
14
 
15
- date: 2012-03-12 00:00:00 +02:00
15
+ date: 2012-04-02 00:00:00 +03:00
16
16
  default_executable:
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
@@ -50,9 +50,13 @@ files:
50
50
  - lib/cloudinary/helper.rb
51
51
  - lib/cloudinary/helpers.rb
52
52
  - lib/cloudinary/migrator.rb
53
+ - lib/cloudinary/missing.rb
54
+ - lib/cloudinary/railtie.rb
55
+ - lib/cloudinary/static.rb
53
56
  - lib/cloudinary/uploader.rb
54
57
  - lib/cloudinary/utils.rb
55
58
  - lib/cloudinary/version.rb
59
+ - lib/tasks/cloudinary.rake
56
60
  has_rdoc: true
57
61
  homepage: http://cloudinary.com
58
62
  licenses: []