cloudinary 1.0.0
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/.gitignore +4 -0
 - data/Gemfile +4 -0
 - data/README.md +46 -0
 - data/Rakefile +3 -0
 - data/cloudinary.gemspec +22 -0
 - data/lib/cloudinary.rb +85 -0
 - data/lib/cloudinary/blob.rb +11 -0
 - data/lib/cloudinary/carrier_wave.rb +228 -0
 - data/lib/cloudinary/downloader.rb +21 -0
 - data/lib/cloudinary/helper.rb +53 -0
 - data/lib/cloudinary/helpers.rb +7 -0
 - data/lib/cloudinary/migrator.rb +361 -0
 - data/lib/cloudinary/uploader.rb +114 -0
 - data/lib/cloudinary/utils.rb +98 -0
 - data/lib/cloudinary/version.rb +4 -0
 - metadata +85 -0
 
    
        data/.gitignore
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
            ==========
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            Cloudinary allows web applications to manage web resources in the cloud leveraging cloud-solutions. 
         
     | 
| 
      
 5 
     | 
    
         
            +
            Cloudinary offers a solution to the entire asset management workflow, from upload to transformations, optimizations, storage and delivery. 
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            ## Setup ######################################################################
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            To install the Cloudinary Ruby GEM, run:
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            	$ gem install cloudinary
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            I you use Rails 3.x or higher, edit your Gemfile, add the following line and run 'bundle'
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            	$ gem 'cloudinary'
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            Or in Rails 2.x, edit your environment.rb and add:
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            	$ config.gem 'cloudinary'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            If you would like to use our optional integration module of image uploads with ActiveRecord using CarrierWave, install CarrierWave to:
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            	$ gem install carrierwave
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            Rails 3.x Gemfile:
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            	$ gem 'carrierwave'
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            Rails 2.x environment.rb
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            	$ config.gem 'carrierwave', :version => '~> 0.4.1'
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            ## Usage ######################################################################
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            Refer to Cloudinary Documentation at:
         
     | 
| 
      
 37 
     | 
    
         
            +
            http://cloudinary.com/documentation
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            For documetation about Ruby on Rails integration see:
         
     | 
| 
      
 41 
     | 
    
         
            +
            http://cloudinary.com/documentation/rails_integration
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            ## License #######################################################################
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            Released under the MIT license. 
         
     | 
    
        data/Rakefile
    ADDED
    
    
    
        data/cloudinary.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # -*- encoding: utf-8 -*-
         
     | 
| 
      
 2 
     | 
    
         
            +
            $:.push File.expand_path("../lib", __FILE__)
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "cloudinary/version"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Gem::Specification.new do |s|
         
     | 
| 
      
 6 
     | 
    
         
            +
              s.name        = "cloudinary"
         
     | 
| 
      
 7 
     | 
    
         
            +
              s.version     = Cloudinary::VERSION
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.authors     = ["Nadav Soferman","Itai Lahan","Tal Lev-Ami"]
         
     | 
| 
      
 9 
     | 
    
         
            +
              s.email       = ["nadav.soferman@cloudinary.com","itai.lahan@cloudinary.com","tal.levami@cloudinary.com"]
         
     | 
| 
      
 10 
     | 
    
         
            +
              s.homepage    = "http://cloudinary.com"
         
     | 
| 
      
 11 
     | 
    
         
            +
              s.summary     = %q{Client library for easily using the Cloudinary service}
         
     | 
| 
      
 12 
     | 
    
         
            +
              s.description = %q{Client library for easily using the Cloudinary service}
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              s.rubyforge_project = "cloudinary"
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              s.files         = `git ls-files`.split("\n")
         
     | 
| 
      
 17 
     | 
    
         
            +
              s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
         
     | 
| 
      
 18 
     | 
    
         
            +
              s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         
     | 
| 
      
 19 
     | 
    
         
            +
              s.require_paths = ["lib"]
         
     | 
| 
      
 20 
     | 
    
         
            +
              
         
     | 
| 
      
 21 
     | 
    
         
            +
              s.add_dependency "rest-client"
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/cloudinary.rb
    ADDED
    
    | 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "ostruct"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "cloudinary/version"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "cloudinary/utils"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "cloudinary/uploader"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "cloudinary/downloader"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "cloudinary/migrator"  
         
     | 
| 
      
 8 
     | 
    
         
            +
            require "cloudinary/blob" 
         
     | 
| 
      
 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
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            module Cloudinary  
         
     | 
| 
      
 65 
     | 
    
         
            +
              @@config = nil
         
     | 
| 
      
 66 
     | 
    
         
            +
              
         
     | 
| 
      
 67 
     | 
    
         
            +
              def self.config(new_config=nil)    
         
     | 
| 
      
 68 
     | 
    
         
            +
                @@config = new_config if new_config
         
     | 
| 
      
 69 
     | 
    
         
            +
                if block_given?
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @@config = OpenStruct.new
         
     | 
| 
      
 71 
     | 
    
         
            +
                  yield(@@config)
         
     | 
| 
      
 72 
     | 
    
         
            +
                end 
         
     | 
| 
      
 73 
     | 
    
         
            +
                # Heroku support
         
     | 
| 
      
 74 
     | 
    
         
            +
                if @@config.nil? && ENV["CLOUDINARY_CLOUD_NAME"]
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @@config = OpenStruct.new(
         
     | 
| 
      
 76 
     | 
    
         
            +
                    "cloud_name" => ENV["CLOUDINARY_CLOUD_NAME"],
         
     | 
| 
      
 77 
     | 
    
         
            +
                    "api_key" => ENV["CLOUDINARY_API_KEY"],
         
     | 
| 
      
 78 
     | 
    
         
            +
                    "api_secret" => ENV["CLOUDINARY_API_SECRET"],
         
     | 
| 
      
 79 
     | 
    
         
            +
                    "secure_distribution" => ENV["CLOUDINARY_SECURE_DISTRIBUTION"],
         
     | 
| 
      
 80 
     | 
    
         
            +
                    "private_cdn" => ENV["CLOUDINARY_PRIVATE_CDN"].to_s == 'true'
         
     | 
| 
      
 81 
     | 
    
         
            +
                  )
         
     | 
| 
      
 82 
     | 
    
         
            +
                end    
         
     | 
| 
      
 83 
     | 
    
         
            +
                @@config ||= OpenStruct.new((YAML.load_file(Rails.root.join("config").join("cloudinary.yml"))[Rails.env] rescue {}))
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
            class Cloudinary::Blob < StringIO
         
     | 
| 
      
 3 
     | 
    
         
            +
              attr_reader :original_filename, :content_type
         
     | 
| 
      
 4 
     | 
    
         
            +
              alias_method :path, :original_filename
         
     | 
| 
      
 5 
     | 
    
         
            +
              
         
     | 
| 
      
 6 
     | 
    
         
            +
              def initialize(data, options={})
         
     | 
| 
      
 7 
     | 
    
         
            +
                super(data)
         
     | 
| 
      
 8 
     | 
    
         
            +
                @original_filename = options[:original_filename] || "cloudinaryfile"
         
     | 
| 
      
 9 
     | 
    
         
            +
                @content_type = options[:content_type] || "application/octet-stream"
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,228 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'pp'
         
     | 
| 
      
 3 
     | 
    
         
            +
            module Cloudinary::CarrierWave
         
     | 
| 
      
 4 
     | 
    
         
            +
              class UploadError < StandardError
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :http_code
         
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(message, http_code)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  super(message)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @http_code = http_code
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
              
         
     | 
| 
      
 12 
     | 
    
         
            +
              module ClassMethods
         
     | 
| 
      
 13 
     | 
    
         
            +
                def eager
         
     | 
| 
      
 14 
     | 
    
         
            +
                  process :eager => true
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
                
         
     | 
| 
      
 17 
     | 
    
         
            +
                def convert(format)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  process :convert => format
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def resize_to_limit(width, height)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  process :resize_to_limit => [width, height]
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def resize_to_fit(width, height)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  process :resize_to_fit => [width, height]
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def resize_to_fill(width, height, gravity="Center")
         
     | 
| 
      
 30 
     | 
    
         
            +
                  process :resize_to_fill => [width, height, gravity]
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def resize_and_pad(width, height, background=:transparent, gravity="Center")
         
     | 
| 
      
 34 
     | 
    
         
            +
                  process :resize_and_pad => [width, height, background, gravity]
         
     | 
| 
      
 35 
     | 
    
         
            +
                end    
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def scale(width, height)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  process :scale => [width, height]
         
     | 
| 
      
 39 
     | 
    
         
            +
                end    
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def crop(width, height, gravity="Center")
         
     | 
| 
      
 42 
     | 
    
         
            +
                  process :crop => [width, height, gravity]
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def cloudinary_transformation(options)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  process :cloudinary_transformation => options
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
                
         
     | 
| 
      
 49 
     | 
    
         
            +
                def tags(*tags)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  process :tags=>tags
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
              
         
     | 
| 
      
 54 
     | 
    
         
            +
              def self.included(base)
         
     | 
| 
      
 55 
     | 
    
         
            +
                base.storage Cloudinary::CarrierWave::Storage
         
     | 
| 
      
 56 
     | 
    
         
            +
                base.extend ClassMethods
         
     | 
| 
      
 57 
     | 
    
         
            +
                base.send(:attr_accessor, :metadata)
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
              
         
     | 
| 
      
 60 
     | 
    
         
            +
              def set_or_yell(hash, attr, value)
         
     | 
| 
      
 61 
     | 
    
         
            +
                raise "conflicting transformation on #{attr} #{value}!=#{hash[attr]}" if hash[attr]
         
     | 
| 
      
 62 
     | 
    
         
            +
                hash[attr] = value
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
              
         
     | 
| 
      
 65 
     | 
    
         
            +
              def transformation
         
     | 
| 
      
 66 
     | 
    
         
            +
                return @transformation if @transformation
         
     | 
| 
      
 67 
     | 
    
         
            +
                transformation = {}
         
     | 
| 
      
 68 
     | 
    
         
            +
                self.class.processors.each do
         
     | 
| 
      
 69 
     | 
    
         
            +
                  |name, args|
         
     | 
| 
      
 70 
     | 
    
         
            +
                  case name
         
     | 
| 
      
 71 
     | 
    
         
            +
                  when :convert # Do nothing. This is handled by format
         
     | 
| 
      
 72 
     | 
    
         
            +
                  when :resize_to_limit 
         
     | 
| 
      
 73 
     | 
    
         
            +
                    set_or_yell(transformation, :width, args[0])    
         
     | 
| 
      
 74 
     | 
    
         
            +
                    set_or_yell(transformation, :height, args[1])
         
     | 
| 
      
 75 
     | 
    
         
            +
                    set_or_yell(transformation, :crop, :limit)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  when :resize_to_fit 
         
     | 
| 
      
 77 
     | 
    
         
            +
                    set_or_yell(transformation, :width, args[0])    
         
     | 
| 
      
 78 
     | 
    
         
            +
                    set_or_yell(transformation, :height, args[1])
         
     | 
| 
      
 79 
     | 
    
         
            +
                    set_or_yell(transformation, :crop, :fit)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  when :resize_to_fill
         
     | 
| 
      
 81 
     | 
    
         
            +
                    set_or_yell(transformation, :width, args[0])    
         
     | 
| 
      
 82 
     | 
    
         
            +
                    set_or_yell(transformation, :height, args[1])
         
     | 
| 
      
 83 
     | 
    
         
            +
                    set_or_yell(transformation, :gravity, args[2].to_s.downcase)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    set_or_yell(transformation, :crop, :fill)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  when :resize_to_pad
         
     | 
| 
      
 86 
     | 
    
         
            +
                    set_or_yell(transformation, :width, args[0])    
         
     | 
| 
      
 87 
     | 
    
         
            +
                    set_or_yell(transformation, :height, args[1])
         
     | 
| 
      
 88 
     | 
    
         
            +
                    set_or_yell(transformation, :gravity, args[3].to_s.downcase)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    set_or_yell(transformation, :crop, :pad)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  when :scale 
         
     | 
| 
      
 91 
     | 
    
         
            +
                    set_or_yell(transformation, :width, args[0])    
         
     | 
| 
      
 92 
     | 
    
         
            +
                    set_or_yell(transformation, :height, args[1])
         
     | 
| 
      
 93 
     | 
    
         
            +
                    set_or_yell(transformation, :crop, :scale)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  when :crop
         
     | 
| 
      
 95 
     | 
    
         
            +
                    set_or_yell(transformation, :width, args[0])    
         
     | 
| 
      
 96 
     | 
    
         
            +
                    set_or_yell(transformation, :height, args[1])
         
     | 
| 
      
 97 
     | 
    
         
            +
                    set_or_yell(transformation, :gravity, args[2].to_s.downcase)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    set_or_yell(transformation, :crop, :crop)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  when :cloudinary_transformation
         
     | 
| 
      
 100 
     | 
    
         
            +
                    args.each do
         
     | 
| 
      
 101 
     | 
    
         
            +
                      |attr, value|        
         
     | 
| 
      
 102 
     | 
    
         
            +
                      set_or_yell(transformation, attr, value)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    end
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
                @transformation = transformation
         
     | 
| 
      
 107 
     | 
    
         
            +
                @transformation     
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
              
         
     | 
| 
      
 110 
     | 
    
         
            +
              def eager
         
     | 
| 
      
 111 
     | 
    
         
            +
                @eager ||= self.class.processors.any?{|processor| processor[0] == :eager}
         
     | 
| 
      
 112 
     | 
    
         
            +
              end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
              def tags
         
     | 
| 
      
 115 
     | 
    
         
            +
                @tags ||= self.class.processors.select{|processor| processor[0] == :tags}.map(&:last).first
         
     | 
| 
      
 116 
     | 
    
         
            +
              end
         
     | 
| 
      
 117 
     | 
    
         
            +
              
         
     | 
| 
      
 118 
     | 
    
         
            +
              def format
         
     | 
| 
      
 119 
     | 
    
         
            +
                format_processor = self.class.processors.find{|processor| processor[0] == :convert}
         
     | 
| 
      
 120 
     | 
    
         
            +
                if format_processor
         
     | 
| 
      
 121 
     | 
    
         
            +
                  if format_processor[1].is_a?(Array)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    return format_processor[1][0]
         
     | 
| 
      
 123 
     | 
    
         
            +
                  end  
         
     | 
| 
      
 124 
     | 
    
         
            +
                  return format_processor[1]
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
                the_filename = original_filename || stored_filename
         
     | 
| 
      
 127 
     | 
    
         
            +
                return the_filename.split(".").last if the_filename.include?(".")
         
     | 
| 
      
 128 
     | 
    
         
            +
                "png" # TODO Default format?
         
     | 
| 
      
 129 
     | 
    
         
            +
              end
         
     | 
| 
      
 130 
     | 
    
         
            +
              
         
     | 
| 
      
 131 
     | 
    
         
            +
              def url(*args)
         
     | 
| 
      
 132 
     | 
    
         
            +
                if(args.first)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  super
         
     | 
| 
      
 134 
     | 
    
         
            +
                else
         
     | 
| 
      
 135 
     | 
    
         
            +
                  options = self.class.version_names.blank? ? {} : self.transformation
         
     | 
| 
      
 136 
     | 
    
         
            +
                  Cloudinary::Utils.cloudinary_url(self.my_filename, options.clone)
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
              def process!(new_file=nil)
         
     | 
| 
      
 141 
     | 
    
         
            +
                # Do nothing
         
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
              def stored_filename
         
     | 
| 
      
 145 
     | 
    
         
            +
                @stored_filename ||= model.read_uploader(mounted_as)
         
     | 
| 
      
 146 
     | 
    
         
            +
              end  
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
              def my_filename
         
     | 
| 
      
 149 
     | 
    
         
            +
                @my_filename ||= stored_filename || ("#{self.public_id}.#{self.format}")
         
     | 
| 
      
 150 
     | 
    
         
            +
              end
         
     | 
| 
      
 151 
     | 
    
         
            +
                
         
     | 
| 
      
 152 
     | 
    
         
            +
              def public_id
         
     | 
| 
      
 153 
     | 
    
         
            +
                return @public_id if @public_id
         
     | 
| 
      
 154 
     | 
    
         
            +
                if stored_filename
         
     | 
| 
      
 155 
     | 
    
         
            +
                  last_dot = stored_filename.rindex(".")
         
     | 
| 
      
 156 
     | 
    
         
            +
                  @public_id = last_dot ? stored_filename[0, last_dot] : stored_filename 
         
     | 
| 
      
 157 
     | 
    
         
            +
                end    
         
     | 
| 
      
 158 
     | 
    
         
            +
                @public_id ||= Cloudinary::Utils.random_public_id
         
     | 
| 
      
 159 
     | 
    
         
            +
              end  
         
     | 
| 
      
 160 
     | 
    
         
            +
              
         
     | 
| 
      
 161 
     | 
    
         
            +
              def download!(uri)
         
     | 
| 
      
 162 
     | 
    
         
            +
                uri = process_uri(uri)
         
     | 
| 
      
 163 
     | 
    
         
            +
                self.original_filename = @cache_id = @filename = File.basename(uri.path).gsub(/[^a-zA-Z0-9\.\-\+_]/, '')
         
     | 
| 
      
 164 
     | 
    
         
            +
                @file = RemoteFile.new(uri, @filename)
         
     | 
| 
      
 165 
     | 
    
         
            +
              end
         
     | 
| 
      
 166 
     | 
    
         
            +
              
         
     | 
| 
      
 167 
     | 
    
         
            +
              def blank?
         
     | 
| 
      
 168 
     | 
    
         
            +
                self.filename.blank? && self.stored_filename.blank?
         
     | 
| 
      
 169 
     | 
    
         
            +
              end
         
     | 
| 
      
 170 
     | 
    
         
            +
              
         
     | 
| 
      
 171 
     | 
    
         
            +
              class RemoteFile
         
     | 
| 
      
 172 
     | 
    
         
            +
                attr_reader :uri, :original_filename
         
     | 
| 
      
 173 
     | 
    
         
            +
                def initialize(uri, filename)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  @uri = uri
         
     | 
| 
      
 175 
     | 
    
         
            +
                  @original_filename = filename
         
     | 
| 
      
 176 
     | 
    
         
            +
                end
         
     | 
| 
      
 177 
     | 
    
         
            +
                
         
     | 
| 
      
 178 
     | 
    
         
            +
                def delete
         
     | 
| 
      
 179 
     | 
    
         
            +
                  # Do nothing. This is a virtual file.
         
     | 
| 
      
 180 
     | 
    
         
            +
                end
         
     | 
| 
      
 181 
     | 
    
         
            +
              end
         
     | 
| 
      
 182 
     | 
    
         
            +
              
         
     | 
| 
      
 183 
     | 
    
         
            +
              class Storage < ::CarrierWave::Storage::Abstract
         
     | 
| 
      
 184 
     | 
    
         
            +
                def store!(file)
         
     | 
| 
      
 185 
     | 
    
         
            +
                  # Moved to identifier...
         
     | 
| 
      
 186 
     | 
    
         
            +
                  if uploader.class.version_names.blank?
         
     | 
| 
      
 187 
     | 
    
         
            +
                    # This is the toplevel, need to upload the actual file.     
         
     | 
| 
      
 188 
     | 
    
         
            +
                    params = uploader.transformation.dup
         
     | 
| 
      
 189 
     | 
    
         
            +
                    params[:return_error] = true
         
     | 
| 
      
 190 
     | 
    
         
            +
                    params[:format] = uploader.format
         
     | 
| 
      
 191 
     | 
    
         
            +
                    params[:public_id] = uploader.public_id.split("/").last
         
     | 
| 
      
 192 
     | 
    
         
            +
                    params[:tags] = uploader.tags if uploader.tags 
         
     | 
| 
      
 193 
     | 
    
         
            +
                    eager_versions = uploader.versions.values.select(&:eager)
         
     | 
| 
      
 194 
     | 
    
         
            +
                    params[:eager] = eager_versions.map{|version| [version.transformation, version.format]} if eager_versions.length > 0
         
     | 
| 
      
 195 
     | 
    
         
            +
                    
         
     | 
| 
      
 196 
     | 
    
         
            +
                    data = nil
         
     | 
| 
      
 197 
     | 
    
         
            +
                    if (file.is_a?(RemoteFile))
         
     | 
| 
      
 198 
     | 
    
         
            +
                      data = file.uri.to_s
         
     | 
| 
      
 199 
     | 
    
         
            +
                    else
         
     | 
| 
      
 200 
     | 
    
         
            +
                      data = file.file
         
     | 
| 
      
 201 
     | 
    
         
            +
                      data.rewind if !file.is_path? && data.respond_to?(:rewind)
         
     | 
| 
      
 202 
     | 
    
         
            +
                    end
         
     | 
| 
      
 203 
     | 
    
         
            +
                    uploader.metadata = Cloudinary::Uploader.upload(data, params)
         
     | 
| 
      
 204 
     | 
    
         
            +
                    if uploader.metadata["error"]
         
     | 
| 
      
 205 
     | 
    
         
            +
                      raise UploadError.new(uploader.metadata["error"]["message"], uploader.metadata["error"]["http_code"])
         
     | 
| 
      
 206 
     | 
    
         
            +
                    end
         
     | 
| 
      
 207 
     | 
    
         
            +
                    
         
     | 
| 
      
 208 
     | 
    
         
            +
                    if uploader.metadata["version"]
         
     | 
| 
      
 209 
     | 
    
         
            +
                      raise "Only ActiveRecord supported at the moment!" if !(uploader.model.class.respond_to?(:update_all) && uploader.model.class.respond_to?(:primary_key))
         
     | 
| 
      
 210 
     | 
    
         
            +
                      primary_key = uploader.model.class.primary_key.to_sym
         
     | 
| 
      
 211 
     | 
    
         
            +
                      uploader.model.class.update_all(["#{uploader.mounted_as}=?", "v#{uploader.metadata["version"]}/#{identifier.split("/").last}"], {primary_key=>uploader.model.send(primary_key)})
         
     | 
| 
      
 212 
     | 
    
         
            +
                    end
         
     | 
| 
      
 213 
     | 
    
         
            +
                    # Will throw an exception on error
         
     | 
| 
      
 214 
     | 
    
         
            +
                  else
         
     | 
| 
      
 215 
     | 
    
         
            +
                    raise "nested versions are not allowed." if (uploader.class.version_names.length > 1)
         
     | 
| 
      
 216 
     | 
    
         
            +
                    # Do nothing
         
     | 
| 
      
 217 
     | 
    
         
            +
                  end
         
     | 
| 
      
 218 
     | 
    
         
            +
                end
         
     | 
| 
      
 219 
     | 
    
         
            +
                
         
     | 
| 
      
 220 
     | 
    
         
            +
                def retrieve!(identifier)
         
     | 
| 
      
 221 
     | 
    
         
            +
                  # Do nothing
         
     | 
| 
      
 222 
     | 
    
         
            +
                end 
         
     | 
| 
      
 223 
     | 
    
         
            +
                
         
     | 
| 
      
 224 
     | 
    
         
            +
                def identifier
         
     | 
| 
      
 225 
     | 
    
         
            +
                  (uploader.filename || uploader.stored_filename) ? uploader.my_filename : nil
         
     | 
| 
      
 226 
     | 
    
         
            +
                end   
         
     | 
| 
      
 227 
     | 
    
         
            +
              end
         
     | 
| 
      
 228 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
            class Cloudinary::Downloader
         
     | 
| 
      
 3 
     | 
    
         
            +
              
         
     | 
| 
      
 4 
     | 
    
         
            +
              def self.download(source, options={})
         
     | 
| 
      
 5 
     | 
    
         
            +
                options = options.clone
         
     | 
| 
      
 6 
     | 
    
         
            +
                if !source.match(/^https?:\/\//i)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  source = Cloudinary::Utils.cloudinary_url(source, options)
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
                
         
     | 
| 
      
 10 
     | 
    
         
            +
                url = URI.parse(source) 
         
     | 
| 
      
 11 
     | 
    
         
            +
                http = Net::HTTP.new(url.host, url.port)
         
     | 
| 
      
 12 
     | 
    
         
            +
                req = Net::HTTP::Get.new(url.path)
         
     | 
| 
      
 13 
     | 
    
         
            +
                if url.port == 443
         
     | 
| 
      
 14 
     | 
    
         
            +
                  http.use_ssl=true
         
     | 
| 
      
 15 
     | 
    
         
            +
                  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
         
     | 
| 
      
 16 
     | 
    
         
            +
                end  
         
     | 
| 
      
 17 
     | 
    
         
            +
                res = http.start{|agent| agent.request(req)}
         
     | 
| 
      
 18 
     | 
    
         
            +
                return res.body        
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module CloudinaryHelper  
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Examples
         
     | 
| 
      
 5 
     | 
    
         
            +
              # cl_image_tag "israel.png", :width=>100, :height=>100, :alt=>"hello" # W/H are not sent to cloudinary
         
     | 
| 
      
 6 
     | 
    
         
            +
              # cl_image_tag "israel.png", :width=>100, :height=>100, :alt=>"hello", :crop=>:fit # W/H are sent to cloudinary
         
     | 
| 
      
 7 
     | 
    
         
            +
              def cl_image_tag(source, options = {})
         
     | 
| 
      
 8 
     | 
    
         
            +
                options = options.clone
         
     | 
| 
      
 9 
     | 
    
         
            +
                source = cloudinary_url(source, options)
         
     | 
| 
      
 10 
     | 
    
         
            +
                options[:width] = options.delete(:html_width) if options.include?(:html_width)
         
     | 
| 
      
 11 
     | 
    
         
            +
                options[:height] = options.delete(:html_height) if options.include?(:html_height)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                image_tag(source, options)
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
              
         
     | 
| 
      
 16 
     | 
    
         
            +
              def facebook_profile_image_tag(profile, options = {})    
         
     | 
| 
      
 17 
     | 
    
         
            +
                cl_image_tag(profile, {:type=>:facebook}.merge(options))
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              def twitter_profile_image_tag(profile, options = {})    
         
     | 
| 
      
 21 
     | 
    
         
            +
                cl_image_tag(profile, {:type=>:twitter}.merge(options))
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              def twitter_name_profile_image_tag(profile, options = {})    
         
     | 
| 
      
 25 
     | 
    
         
            +
                cl_image_tag(profile, {:type=>:twitter_name}.merge(options))
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              def cl_sprite_url(source, options = {})
         
     | 
| 
      
 29 
     | 
    
         
            +
                options = options.clone
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                version_store = options.delete(:version_store)
         
     | 
| 
      
 32 
     | 
    
         
            +
                if options[:version].blank? && (version_store == :file) && defined?(Rails) && defined?(Rails.root)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  file_name = "#{Rails.root}/tmp/cloudinary/cloudinary_sprite_#{source.sub(/\..*/, '')}.version"
         
     | 
| 
      
 34 
     | 
    
         
            +
                  if File.exists?(file_name)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    options[:version] = File.read(file_name).chomp        
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end  
         
     | 
| 
      
 38 
     | 
    
         
            +
                
         
     | 
| 
      
 39 
     | 
    
         
            +
                options[:format] = "css" unless source.ends_with?(".css")
         
     | 
| 
      
 40 
     | 
    
         
            +
                cloudinary_url(source, options.merge(:type=>:sprite))
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              def cl_sprite_tag(source, options = {})
         
     | 
| 
      
 44 
     | 
    
         
            +
                stylesheet_link_tag(cl_sprite_url(source, options))
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              def cloudinary_url(source, options = {})
         
     | 
| 
      
 48 
     | 
    
         
            +
                options[:secure] = request.ssl? if !options.include?(:secure) && defined?(request) && request && request.respond_to?(:ssl?)
         
     | 
| 
      
 49 
     | 
    
         
            +
                Cloudinary::Utils.cloudinary_url(source, options)
         
     | 
| 
      
 50 
     | 
    
         
            +
              end  
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            ActionView::Base.send :include, CloudinaryHelper
         
     | 
| 
         @@ -0,0 +1,361 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Cloudinary::Migrator
         
     | 
| 
      
 4 
     | 
    
         
            +
              attr_reader :retrieve, :complete
         
     | 
| 
      
 5 
     | 
    
         
            +
              attr_accessor :terminate, :in_process
         
     | 
| 
      
 6 
     | 
    
         
            +
              attr_reader :db
         
     | 
| 
      
 7 
     | 
    
         
            +
              attr_reader :work, :results, :mutex
         
     | 
| 
      
 8 
     | 
    
         
            +
              attr_reader :extra_options
         
     | 
| 
      
 9 
     | 
    
         
            +
              
         
     | 
| 
      
 10 
     | 
    
         
            +
              
         
     | 
| 
      
 11 
     | 
    
         
            +
              @@init = false
         
     | 
| 
      
 12 
     | 
    
         
            +
              def self.init
         
     | 
| 
      
 13 
     | 
    
         
            +
                return if @@init
         
     | 
| 
      
 14 
     | 
    
         
            +
                @@init = true
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                begin 
         
     | 
| 
      
 17 
     | 
    
         
            +
                  require 'sqlite3'
         
     | 
| 
      
 18 
     | 
    
         
            +
                rescue LoadError
         
     | 
| 
      
 19 
     | 
    
         
            +
                  raise "Please add sqlite3 to your Gemfile"    
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
                require 'tempfile'
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
              
         
     | 
| 
      
 24 
     | 
    
         
            +
              def json_decode(str)
         
     | 
| 
      
 25 
     | 
    
         
            +
                Cloudinary::Utils.json_decode(str)
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
                
         
     | 
| 
      
 28 
     | 
    
         
            +
              def initialize(options={})
         
     | 
| 
      
 29 
     | 
    
         
            +
                self.class.init
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                options[:db_file] = "tmp/migration#{$$}.db" if options[:private_database] && !options[:db_file] 
         
     | 
| 
      
 32 
     | 
    
         
            +
                @dbfile = options[:db_file] || "tmp/migration.db"
         
     | 
| 
      
 33 
     | 
    
         
            +
                FileUtils.mkdir_p(File.dirname(@dbfile))
         
     | 
| 
      
 34 
     | 
    
         
            +
                @db = SQLite3::Database.new @dbfile, :results_as_hash=>true
         
     | 
| 
      
 35 
     | 
    
         
            +
                @retrieve = options[:retrieve]
         
     | 
| 
      
 36 
     | 
    
         
            +
                @complete = options[:complete]
         
     | 
| 
      
 37 
     | 
    
         
            +
                @debug = options[:debug] || false
         
     | 
| 
      
 38 
     | 
    
         
            +
                @ignore_duplicates = options[:ignore_duplicates]
         
     | 
| 
      
 39 
     | 
    
         
            +
                @threads = [options[:threads] || 10, 100].min
         
     | 
| 
      
 40 
     | 
    
         
            +
                @extra_options = {:api_key=>options[:api_key], :api_secret=>options[:api_secret]}
         
     | 
| 
      
 41 
     | 
    
         
            +
                @delete_after_done = options[:delete_after_done] || options[:private_database]
         
     | 
| 
      
 42 
     | 
    
         
            +
                @max_processing = @threads * 10
         
     | 
| 
      
 43 
     | 
    
         
            +
                @in_process = 0
         
     | 
| 
      
 44 
     | 
    
         
            +
                @work = Queue.new
         
     | 
| 
      
 45 
     | 
    
         
            +
                @results = Queue.new
         
     | 
| 
      
 46 
     | 
    
         
            +
                @mutex = Mutex.new
         
     | 
| 
      
 47 
     | 
    
         
            +
                @db.execute "
         
     | 
| 
      
 48 
     | 
    
         
            +
                  create table if not exists queue (
         
     | 
| 
      
 49 
     | 
    
         
            +
                    id integer primary key,
         
     | 
| 
      
 50 
     | 
    
         
            +
                    internal_id integer,
         
     | 
| 
      
 51 
     | 
    
         
            +
                    public_id text,
         
     | 
| 
      
 52 
     | 
    
         
            +
                    url text,
         
     | 
| 
      
 53 
     | 
    
         
            +
                    metadata text,
         
     | 
| 
      
 54 
     | 
    
         
            +
                    result string,
         
     | 
| 
      
 55 
     | 
    
         
            +
                    status text,
         
     | 
| 
      
 56 
     | 
    
         
            +
                    updated_at integer
         
     | 
| 
      
 57 
     | 
    
         
            +
                  )
         
     | 
| 
      
 58 
     | 
    
         
            +
                "
         
     | 
| 
      
 59 
     | 
    
         
            +
                @db.execute "
         
     | 
| 
      
 60 
     | 
    
         
            +
                  create index if not exists status_idx on queue (
         
     | 
| 
      
 61 
     | 
    
         
            +
                    status
         
     | 
| 
      
 62 
     | 
    
         
            +
                  )
         
     | 
| 
      
 63 
     | 
    
         
            +
                "
         
     | 
| 
      
 64 
     | 
    
         
            +
                @db.execute "
         
     | 
| 
      
 65 
     | 
    
         
            +
                  create unique index if not exists internal_id_idx on queue (
         
     | 
| 
      
 66 
     | 
    
         
            +
                    internal_id
         
     | 
| 
      
 67 
     | 
    
         
            +
                  )
         
     | 
| 
      
 68 
     | 
    
         
            +
                "
         
     | 
| 
      
 69 
     | 
    
         
            +
                @db.execute "
         
     | 
| 
      
 70 
     | 
    
         
            +
                  create unique index if not exists public_id_idx on queue (
         
     | 
| 
      
 71 
     | 
    
         
            +
                    public_id
         
     | 
| 
      
 72 
     | 
    
         
            +
                  )
         
     | 
| 
      
 73 
     | 
    
         
            +
                "
         
     | 
| 
      
 74 
     | 
    
         
            +
                if options[:reset_queue]
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @db.execute("delete from queue")
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
              def register_retrieve(&block)
         
     | 
| 
      
 80 
     | 
    
         
            +
                @retrieve = block
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
              def register_complete(&block)
         
     | 
| 
      
 84 
     | 
    
         
            +
                @complete = block
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
              
         
     | 
| 
      
 87 
     | 
    
         
            +
              def process(options={})    
         
     | 
| 
      
 88 
     | 
    
         
            +
                raise "url not given and no retieve callback given" if options[:url].nil? && self.retrieve.nil?
         
     | 
| 
      
 89 
     | 
    
         
            +
                raise "id not given and retieve or complete callback given" if options[:id].nil? && (!self.retrieve.nil? || !self.complete.nil?)
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                debug("Process: #{options.inspect}")
         
     | 
| 
      
 92 
     | 
    
         
            +
                start
         
     | 
| 
      
 93 
     | 
    
         
            +
                process_results    
         
     | 
| 
      
 94 
     | 
    
         
            +
                wait_for_queue
         
     | 
| 
      
 95 
     | 
    
         
            +
                options = options.dup
         
     | 
| 
      
 96 
     | 
    
         
            +
                id = options.delete(:id)
         
     | 
| 
      
 97 
     | 
    
         
            +
                url = options.delete(:url)
         
     | 
| 
      
 98 
     | 
    
         
            +
                public_id = options.delete(:public_id)
         
     | 
| 
      
 99 
     | 
    
         
            +
                row = {
         
     | 
| 
      
 100 
     | 
    
         
            +
                  "internal_id"=>id, 
         
     | 
| 
      
 101 
     | 
    
         
            +
                  "url"=>url, 
         
     | 
| 
      
 102 
     | 
    
         
            +
                  "public_id"=>public_id, 
         
     | 
| 
      
 103 
     | 
    
         
            +
                  "metadata"=>options.to_json,
         
     | 
| 
      
 104 
     | 
    
         
            +
                  "status"=>"processing"      
         
     | 
| 
      
 105 
     | 
    
         
            +
                }    
         
     | 
| 
      
 106 
     | 
    
         
            +
                begin
         
     | 
| 
      
 107 
     | 
    
         
            +
                  insert_row(row)
         
     | 
| 
      
 108 
     | 
    
         
            +
                  add_to_work_queue(row)
         
     | 
| 
      
 109 
     | 
    
         
            +
                rescue SQLite3::ConstraintException
         
     | 
| 
      
 110 
     | 
    
         
            +
                  raise if !@ignore_duplicates
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
              end
         
     | 
| 
      
 113 
     | 
    
         
            +
                  
         
     | 
| 
      
 114 
     | 
    
         
            +
              def done
         
     | 
| 
      
 115 
     | 
    
         
            +
                start
         
     | 
| 
      
 116 
     | 
    
         
            +
                process_all_pending
         
     | 
| 
      
 117 
     | 
    
         
            +
                @terminate = true
         
     | 
| 
      
 118 
     | 
    
         
            +
                1.upto(@threads){self.work << nil} # enough objects to release all waiting threads
         
     | 
| 
      
 119 
     | 
    
         
            +
                @started = false
         
     | 
| 
      
 120 
     | 
    
         
            +
                @db.close
         
     | 
| 
      
 121 
     | 
    
         
            +
                File.delete(@dbfile) if @delete_after_done
         
     | 
| 
      
 122 
     | 
    
         
            +
              end
         
     | 
| 
      
 123 
     | 
    
         
            +
              
         
     | 
| 
      
 124 
     | 
    
         
            +
              def max_given_id
         
     | 
| 
      
 125 
     | 
    
         
            +
                db.get_first_value("select max(internal_id) from queue").to_i
         
     | 
| 
      
 126 
     | 
    
         
            +
              end
         
     | 
| 
      
 127 
     | 
    
         
            +
              
         
     | 
| 
      
 128 
     | 
    
         
            +
              def close_if_needed(file)
         
     | 
| 
      
 129 
     | 
    
         
            +
                if file.nil?
         
     | 
| 
      
 130 
     | 
    
         
            +
                  # Do nothing.
         
     | 
| 
      
 131 
     | 
    
         
            +
                elsif file.respond_to?(:close!) 
         
     | 
| 
      
 132 
     | 
    
         
            +
                  file.close! 
         
     | 
| 
      
 133 
     | 
    
         
            +
                elsif file.respond_to?(:close)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  file.close
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
              rescue
         
     | 
| 
      
 137 
     | 
    
         
            +
                # Ignore errors in closing files
         
     | 
| 
      
 138 
     | 
    
         
            +
              end  
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
              def temporary_file(data, filename)  
         
     | 
| 
      
 141 
     | 
    
         
            +
                file = RUBY_VERSION == "1.8.7" ? Tempfile.new('cloudinary') : Tempfile.new('cloudinary', :encoding => 'ascii-8bit')
         
     | 
| 
      
 142 
     | 
    
         
            +
                file.unlink
         
     | 
| 
      
 143 
     | 
    
         
            +
                file.write(data)
         
     | 
| 
      
 144 
     | 
    
         
            +
                file.rewind
         
     | 
| 
      
 145 
     | 
    
         
            +
                # Tempfile return path == nil after unlink, which break rest-client              
         
     | 
| 
      
 146 
     | 
    
         
            +
                class << file
         
     | 
| 
      
 147 
     | 
    
         
            +
                  attr_accessor :original_filename
         
     | 
| 
      
 148 
     | 
    
         
            +
                  def content_type
         
     | 
| 
      
 149 
     | 
    
         
            +
                    "application/octet-stream"                  
         
     | 
| 
      
 150 
     | 
    
         
            +
                  end
         
     | 
| 
      
 151 
     | 
    
         
            +
                end
         
     | 
| 
      
 152 
     | 
    
         
            +
                file.original_filename = filename
         
     | 
| 
      
 153 
     | 
    
         
            +
                file                  
         
     | 
| 
      
 154 
     | 
    
         
            +
              end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
              private
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
              def update_all(values)
         
     | 
| 
      
 159 
     | 
    
         
            +
                @db.execute("update queue set #{values.keys.map{|key| "#{key}=?"}.join(",")}", *values.values)
         
     | 
| 
      
 160 
     | 
    
         
            +
              end
         
     | 
| 
      
 161 
     | 
    
         
            +
              
         
     | 
| 
      
 162 
     | 
    
         
            +
              def update_row(row, values)    
         
     | 
| 
      
 163 
     | 
    
         
            +
                values.merge!("updated_at"=>Time.now.to_i)
         
     | 
| 
      
 164 
     | 
    
         
            +
                query = ["update queue set #{values.keys.map{|key| "#{key}=?"}.join(",")} where id=?"] + values.values + [row["id"]]
         
     | 
| 
      
 165 
     | 
    
         
            +
                result = @db.execute(*query)
         
     | 
| 
      
 166 
     | 
    
         
            +
                values.each{|key, value| row[key.to_s] = value}
         
     | 
| 
      
 167 
     | 
    
         
            +
                row    
         
     | 
| 
      
 168 
     | 
    
         
            +
              end
         
     | 
| 
      
 169 
     | 
    
         
            +
              
         
     | 
| 
      
 170 
     | 
    
         
            +
              def insert_row(values)
         
     | 
| 
      
 171 
     | 
    
         
            +
                values.merge!("updated_at"=>Time.now.to_i)
         
     | 
| 
      
 172 
     | 
    
         
            +
                @db.execute("insert into queue (#{values.keys.join(",")}) values (#{values.keys.map{"?"}.join(",")})", *values.values)
         
     | 
| 
      
 173 
     | 
    
         
            +
                values["id"] = @db.last_insert_row_id
         
     | 
| 
      
 174 
     | 
    
         
            +
              end
         
     | 
| 
      
 175 
     | 
    
         
            +
              
         
     | 
| 
      
 176 
     | 
    
         
            +
              def refill_queue(last_id)
         
     | 
| 
      
 177 
     | 
    
         
            +
                @db.execute("select * from queue where status in ('error', 'processing') and id > ? limit ?", last_id, 10000) do
         
     | 
| 
      
 178 
     | 
    
         
            +
                  |row|
         
     | 
| 
      
 179 
     | 
    
         
            +
                  last_id = row["id"] if row["id"] > last_id
         
     | 
| 
      
 180 
     | 
    
         
            +
                  wait_for_queue
         
     | 
| 
      
 181 
     | 
    
         
            +
                  add_to_work_queue(row)
         
     | 
| 
      
 182 
     | 
    
         
            +
                end
         
     | 
| 
      
 183 
     | 
    
         
            +
                last_id
         
     | 
| 
      
 184 
     | 
    
         
            +
              end 
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
              def process_results
         
     | 
| 
      
 187 
     | 
    
         
            +
                while self.results.length > 0
         
     | 
| 
      
 188 
     | 
    
         
            +
                  row = self.results.pop
         
     | 
| 
      
 189 
     | 
    
         
            +
                  result = json_decode(row["result"])      
         
     | 
| 
      
 190 
     | 
    
         
            +
                  debug("Done ID=#{row['internal_id']}, result=#{result.inspect}")
         
     | 
| 
      
 191 
     | 
    
         
            +
                  complete.call(row["internal_id"], result) if complete 
         
     | 
| 
      
 192 
     | 
    
         
            +
                  if result["error"]        
         
     | 
| 
      
 193 
     | 
    
         
            +
                    status = case result["error"]["http_code"]
         
     | 
| 
      
 194 
     | 
    
         
            +
                    when 400, 404 then "fatal" # Problematic request. Not a server problem.
         
     | 
| 
      
 195 
     | 
    
         
            +
                    else "error"
         
     | 
| 
      
 196 
     | 
    
         
            +
                    end
         
     | 
| 
      
 197 
     | 
    
         
            +
                  else
         
     | 
| 
      
 198 
     | 
    
         
            +
                    status = "completed"
         
     | 
| 
      
 199 
     | 
    
         
            +
                  end       
         
     | 
| 
      
 200 
     | 
    
         
            +
                  updates = {:status=>status, :result=>row["result"]}
         
     | 
| 
      
 201 
     | 
    
         
            +
                  updates["public_id"] = result["public_id"] if result["public_id"] && !row["public_id"]
         
     | 
| 
      
 202 
     | 
    
         
            +
                  begin 
         
     | 
| 
      
 203 
     | 
    
         
            +
                    update_row(row, updates)
         
     | 
| 
      
 204 
     | 
    
         
            +
                  rescue SQLite3::ConstraintException
         
     | 
| 
      
 205 
     | 
    
         
            +
                    updates = {:status=>"error", :result=>{:error=>{:message=>"public_id already exists"}}.to_json}
         
     | 
| 
      
 206 
     | 
    
         
            +
                    update_row(row, updates)
         
     | 
| 
      
 207 
     | 
    
         
            +
                  end
         
     | 
| 
      
 208 
     | 
    
         
            +
                end
         
     | 
| 
      
 209 
     | 
    
         
            +
              end
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
              def try_try_again(tries=5)
         
     | 
| 
      
 212 
     | 
    
         
            +
                retry_count = 0
         
     | 
| 
      
 213 
     | 
    
         
            +
                begin
         
     | 
| 
      
 214 
     | 
    
         
            +
                  return yield
         
     | 
| 
      
 215 
     | 
    
         
            +
                rescue
         
     | 
| 
      
 216 
     | 
    
         
            +
                  retry_count++
         
     | 
| 
      
 217 
     | 
    
         
            +
                  raise if retry_count > tries
         
     | 
| 
      
 218 
     | 
    
         
            +
                  sleep rand * 3
         
     | 
| 
      
 219 
     | 
    
         
            +
                  retry
         
     | 
| 
      
 220 
     | 
    
         
            +
                end  
         
     | 
| 
      
 221 
     | 
    
         
            +
              end
         
     | 
| 
      
 222 
     | 
    
         
            +
              
         
     | 
| 
      
 223 
     | 
    
         
            +
              def start
         
     | 
| 
      
 224 
     | 
    
         
            +
                return if @started
         
     | 
| 
      
 225 
     | 
    
         
            +
                @started = true
         
     | 
| 
      
 226 
     | 
    
         
            +
                @terminate = false
         
     | 
| 
      
 227 
     | 
    
         
            +
                
         
     | 
| 
      
 228 
     | 
    
         
            +
                self.work.clear
         
     | 
| 
      
 229 
     | 
    
         
            +
                
         
     | 
| 
      
 230 
     | 
    
         
            +
                main = self
         
     | 
| 
      
 231 
     | 
    
         
            +
                Thread.abort_on_exception = true
         
     | 
| 
      
 232 
     | 
    
         
            +
                1.upto(@threads) do
         
     | 
| 
      
 233 
     | 
    
         
            +
                  |i|
         
     | 
| 
      
 234 
     | 
    
         
            +
                  Thread.start do
         
     | 
| 
      
 235 
     | 
    
         
            +
                    while !main.terminate
         
     | 
| 
      
 236 
     | 
    
         
            +
                      file = nil
         
     | 
| 
      
 237 
     | 
    
         
            +
                      row = main.work.pop
         
     | 
| 
      
 238 
     | 
    
         
            +
                      next if row.nil?
         
     | 
| 
      
 239 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 240 
     | 
    
         
            +
                        debug "Thread #{i} - processing row #{row.inspect}. #{main.work.length} work waiting. #{main.results.length} results waiting."
         
     | 
| 
      
 241 
     | 
    
         
            +
                        url = row["url"]
         
     | 
| 
      
 242 
     | 
    
         
            +
                        cw = false
         
     | 
| 
      
 243 
     | 
    
         
            +
                        result = nil
         
     | 
| 
      
 244 
     | 
    
         
            +
                        if url.nil? && !self.retrieve.nil?
         
     | 
| 
      
 245 
     | 
    
         
            +
                          data = self.retrieve.call(row["internal_id"])
         
     | 
| 
      
 246 
     | 
    
         
            +
                          if defined?(ActiveRecord::Base) && data.is_a?(ActiveRecord::Base)
         
     | 
| 
      
 247 
     | 
    
         
            +
                            cw = true
         
     | 
| 
      
 248 
     | 
    
         
            +
                            data.save!
         
     | 
| 
      
 249 
     | 
    
         
            +
                          elsif defined?(Cloudinary::CarrierWave) && data.is_a?(Cloudinary::CarrierWave)
         
     | 
| 
      
 250 
     | 
    
         
            +
                            cw = true
         
     | 
| 
      
 251 
     | 
    
         
            +
                            begin
         
     | 
| 
      
 252 
     | 
    
         
            +
                              data.model.save! 
         
     | 
| 
      
 253 
     | 
    
         
            +
                            rescue Cloudinary::CarrierWave::UploadError 
         
     | 
| 
      
 254 
     | 
    
         
            +
                              # upload errors will be handled by the result values.
         
     | 
| 
      
 255 
     | 
    
         
            +
                            end
         
     | 
| 
      
 256 
     | 
    
         
            +
                            result = data.metadata
         
     | 
| 
      
 257 
     | 
    
         
            +
                          elsif data.respond_to?(:read) && data.respond_to?(:path)
         
     | 
| 
      
 258 
     | 
    
         
            +
                            # This is an IO style object, pass as is.
         
     | 
| 
      
 259 
     | 
    
         
            +
                            file = data
         
     | 
| 
      
 260 
     | 
    
         
            +
                          elsif !data.nil?
         
     | 
| 
      
 261 
     | 
    
         
            +
                            file = main.temporary_file(data, row["public_id"] || "cloudinaryfile") 
         
     | 
| 
      
 262 
     | 
    
         
            +
                          end
         
     | 
| 
      
 263 
     | 
    
         
            +
                        end
         
     | 
| 
      
 264 
     | 
    
         
            +
                        
         
     | 
| 
      
 265 
     | 
    
         
            +
                        if url || file
         
     | 
| 
      
 266 
     | 
    
         
            +
                          options = main.extra_options.merge(:public_id=>row["public_id"])
         
     | 
| 
      
 267 
     | 
    
         
            +
                          json_decode(row["metadata"]).each do
         
     | 
| 
      
 268 
     | 
    
         
            +
                            |key, value|
         
     | 
| 
      
 269 
     | 
    
         
            +
                            options[key.to_sym] = value
         
     | 
| 
      
 270 
     | 
    
         
            +
                          end
         
     | 
| 
      
 271 
     | 
    
         
            +
                                      
         
     | 
| 
      
 272 
     | 
    
         
            +
                          result = Cloudinary::Uploader.upload(url || file, options.merge(:return_error=>true)) || ({:error=>{:message=>"Received nil from uploader!"}})
         
     | 
| 
      
 273 
     | 
    
         
            +
                        elsif cw
         
     | 
| 
      
 274 
     | 
    
         
            +
                          result ||= {"status" => "saved"}
         
     | 
| 
      
 275 
     | 
    
         
            +
                        else
         
     | 
| 
      
 276 
     | 
    
         
            +
                          result = {"error" => {"message" => "Empty data and url", "http_code"=>404}} 
         
     | 
| 
      
 277 
     | 
    
         
            +
                        end
         
     | 
| 
      
 278 
     | 
    
         
            +
                        main.results << {"id"=>row["id"], "internal_id"=>row["internal_id"], "result"=>result.to_json}
         
     | 
| 
      
 279 
     | 
    
         
            +
                      rescue => e
         
     | 
| 
      
 280 
     | 
    
         
            +
                        $stderr.print "Thread #{i} - Error in processing row #{row.inspect} - #{e}\n"
         
     | 
| 
      
 281 
     | 
    
         
            +
                        debug(e.backtrace.join("\n"))
         
     | 
| 
      
 282 
     | 
    
         
            +
                        sleep 1
         
     | 
| 
      
 283 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 284 
     | 
    
         
            +
                        main.mutex.synchronize{main.in_process -= 1}
         
     | 
| 
      
 285 
     | 
    
         
            +
                        main.close_if_needed(file)
         
     | 
| 
      
 286 
     | 
    
         
            +
                      end          
         
     | 
| 
      
 287 
     | 
    
         
            +
                    end
         
     | 
| 
      
 288 
     | 
    
         
            +
                  end
         
     | 
| 
      
 289 
     | 
    
         
            +
                end 
         
     | 
| 
      
 290 
     | 
    
         
            +
                
         
     | 
| 
      
 291 
     | 
    
         
            +
                retry_previous_queue # Retry all work from previous iteration before we start processing this one.
         
     | 
| 
      
 292 
     | 
    
         
            +
              end
         
     | 
| 
      
 293 
     | 
    
         
            +
              
         
     | 
| 
      
 294 
     | 
    
         
            +
              def debug(message)
         
     | 
| 
      
 295 
     | 
    
         
            +
                if @debug
         
     | 
| 
      
 296 
     | 
    
         
            +
                  $stderr.print "#{Time.now} Cloudinary::Migrator #{message}\n"
         
     | 
| 
      
 297 
     | 
    
         
            +
                end
         
     | 
| 
      
 298 
     | 
    
         
            +
              end
         
     | 
| 
      
 299 
     | 
    
         
            +
             
     | 
| 
      
 300 
     | 
    
         
            +
              def retry_previous_queue
         
     | 
| 
      
 301 
     | 
    
         
            +
                last_id = 0
         
     | 
| 
      
 302 
     | 
    
         
            +
                begin
         
     | 
| 
      
 303 
     | 
    
         
            +
                  prev_last_id, last_id = last_id, refill_queue(last_id)
         
     | 
| 
      
 304 
     | 
    
         
            +
                end while last_id > prev_last_id
         
     | 
| 
      
 305 
     | 
    
         
            +
                process_results    
         
     | 
| 
      
 306 
     | 
    
         
            +
              end
         
     | 
| 
      
 307 
     | 
    
         
            +
              
         
     | 
| 
      
 308 
     | 
    
         
            +
              def process_all_pending
         
     | 
| 
      
 309 
     | 
    
         
            +
                # Waiting for work to finish. While we are at it, process results.
         
     | 
| 
      
 310 
     | 
    
         
            +
                while self.in_process > 0      
         
     | 
| 
      
 311 
     | 
    
         
            +
                  process_results
         
     | 
| 
      
 312 
     | 
    
         
            +
                  sleep 0.1
         
     | 
| 
      
 313 
     | 
    
         
            +
                end
         
     | 
| 
      
 314 
     | 
    
         
            +
                # Make sure we processed all the results
         
     | 
| 
      
 315 
     | 
    
         
            +
                process_results
         
     | 
| 
      
 316 
     | 
    
         
            +
              end
         
     | 
| 
      
 317 
     | 
    
         
            +
              
         
     | 
| 
      
 318 
     | 
    
         
            +
              def add_to_work_queue(row)
         
     | 
| 
      
 319 
     | 
    
         
            +
                self.work << row
         
     | 
| 
      
 320 
     | 
    
         
            +
                mutex.synchronize{self.in_process += 1}
         
     | 
| 
      
 321 
     | 
    
         
            +
              end
         
     | 
| 
      
 322 
     | 
    
         
            +
              
         
     | 
| 
      
 323 
     | 
    
         
            +
              def wait_for_queue
         
     | 
| 
      
 324 
     | 
    
         
            +
                # Waiting f
         
     | 
| 
      
 325 
     | 
    
         
            +
                while self.work.length > @max_processing
         
     | 
| 
      
 326 
     | 
    
         
            +
                  process_results    
         
     | 
| 
      
 327 
     | 
    
         
            +
                  sleep 0.1
         
     | 
| 
      
 328 
     | 
    
         
            +
                end    
         
     | 
| 
      
 329 
     | 
    
         
            +
              end
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
              def self.sample        
         
     | 
| 
      
 332 
     | 
    
         
            +
                migrator = Cloudinary::Migrator.new(
         
     | 
| 
      
 333 
     | 
    
         
            +
                  :retrieve=>proc{|id| Post.find(id).data}, 
         
     | 
| 
      
 334 
     | 
    
         
            +
                  :complete=>proc{|id, result| a}
         
     | 
| 
      
 335 
     | 
    
         
            +
                  )
         
     | 
| 
      
 336 
     | 
    
         
            +
                
         
     | 
| 
      
 337 
     | 
    
         
            +
                Post.find_each(:conditions=>["id > ?", migrator.max_given_id], :select=>"id") do
         
     | 
| 
      
 338 
     | 
    
         
            +
                  |post|
         
     | 
| 
      
 339 
     | 
    
         
            +
                  migrator.process(:id=>post.id, :public_id=>"post_#{post.id}")
         
     | 
| 
      
 340 
     | 
    
         
            +
                end
         
     | 
| 
      
 341 
     | 
    
         
            +
                migrator.done
         
     | 
| 
      
 342 
     | 
    
         
            +
              end 
         
     | 
| 
      
 343 
     | 
    
         
            +
                
         
     | 
| 
      
 344 
     | 
    
         
            +
              def self.test  
         
     | 
| 
      
 345 
     | 
    
         
            +
                posts = {}
         
     | 
| 
      
 346 
     | 
    
         
            +
                done = {}      
         
     | 
| 
      
 347 
     | 
    
         
            +
                migrator = Cloudinary::Migrator.new(
         
     | 
| 
      
 348 
     | 
    
         
            +
                  :retrieve=>proc{|id| posts[id]}, 
         
     | 
| 
      
 349 
     | 
    
         
            +
                  :complete=>proc{|id, result| $stderr.print "done #{id} #{result}\n"; done[id] = result}
         
     | 
| 
      
 350 
     | 
    
         
            +
                  )
         
     | 
| 
      
 351 
     | 
    
         
            +
                start = migrator.max_given_id + 1
         
     | 
| 
      
 352 
     | 
    
         
            +
                (start..1000).each{|i| posts[i] = "hello#{i}"}
         
     | 
| 
      
 353 
     | 
    
         
            +
                
         
     | 
| 
      
 354 
     | 
    
         
            +
                posts.each do
         
     | 
| 
      
 355 
     | 
    
         
            +
                  |id, data|
         
     | 
| 
      
 356 
     | 
    
         
            +
                  migrator.process(:id=>id, :public_id=>"post_#{id}")
         
     | 
| 
      
 357 
     | 
    
         
            +
                end
         
     | 
| 
      
 358 
     | 
    
         
            +
                migrator.done
         
     | 
| 
      
 359 
     | 
    
         
            +
                pp [done.length, start]
         
     | 
| 
      
 360 
     | 
    
         
            +
              end        
         
     | 
| 
      
 361 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,114 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rest_client'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class Cloudinary::Uploader
         
     | 
| 
      
 6 
     | 
    
         
            +
              
         
     | 
| 
      
 7 
     | 
    
         
            +
              def self.upload(file, options={})
         
     | 
| 
      
 8 
     | 
    
         
            +
                call_api("upload", options) do    
         
     | 
| 
      
 9 
     | 
    
         
            +
                  params = {:timestamp=>Time.now.to_i,
         
     | 
| 
      
 10 
     | 
    
         
            +
                            :transformation => Cloudinary::Utils.generate_transformation_string(options),
         
     | 
| 
      
 11 
     | 
    
         
            +
                            :public_id=> options[:public_id],
         
     | 
| 
      
 12 
     | 
    
         
            +
                            :format=>options[:format],
         
     | 
| 
      
 13 
     | 
    
         
            +
                            :tags=>options[:tags] && Array(options[:tags]).join(",")}.reject{|k,v| v.blank?}    
         
     | 
| 
      
 14 
     | 
    
         
            +
                  if options[:eager]
         
     | 
| 
      
 15 
     | 
    
         
            +
                    params[:eager] = options[:eager].map do
         
     | 
| 
      
 16 
     | 
    
         
            +
                      |transformation|
         
     | 
| 
      
 17 
     | 
    
         
            +
                      transformation, format = Array(transformation) # format is optional
         
     | 
| 
      
 18 
     | 
    
         
            +
                      [Cloudinary::Utils.generate_transformation_string(transformation.clone), format].compact.join("/")
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end.join("|")
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  if file.respond_to?(:read) || file =~ /^https?:/
         
     | 
| 
      
 22 
     | 
    
         
            +
                    params[:file] = file
         
     | 
| 
      
 23 
     | 
    
         
            +
                  else 
         
     | 
| 
      
 24 
     | 
    
         
            +
                    params[:file] = File.open(file, "rb")
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  [params, [:file]]
         
     | 
| 
      
 27 
     | 
    
         
            +
                end              
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
                
         
     | 
| 
      
 30 
     | 
    
         
            +
              def self.generate_sprite(tag, options={})
         
     | 
| 
      
 31 
     | 
    
         
            +
                version_store = options.delete(:version_store)
         
     | 
| 
      
 32 
     | 
    
         
            +
                
         
     | 
| 
      
 33 
     | 
    
         
            +
                result = call_api("sprite", options) do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  {
         
     | 
| 
      
 35 
     | 
    
         
            +
                    :timestamp=>Time.now.to_i,
         
     | 
| 
      
 36 
     | 
    
         
            +
                    :tag=>tag,
         
     | 
| 
      
 37 
     | 
    
         
            +
                    :transformation => Cloudinary::Utils.generate_transformation_string(options)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  }    
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
                
         
     | 
| 
      
 41 
     | 
    
         
            +
                if version_store == :file && result && result["version"]
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if defined?(Rails) && defined?(Rails.root)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    FileUtils.mkdir_p("#{Rails.root}/tmp/cloudinary")
         
     | 
| 
      
 44 
     | 
    
         
            +
                    File.open("#{Rails.root}/tmp/cloudinary/cloudinary_sprite_#{tag}.version", "w"){|file| file.print result["version"].to_s}                      
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end  
         
     | 
| 
      
 46 
     | 
    
         
            +
                end      
         
     | 
| 
      
 47 
     | 
    
         
            +
                return result
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
              
         
     | 
| 
      
 50 
     | 
    
         
            +
              # options may include 'exclusive' (boolean) which causes clearing this tag from all other resources 
         
     | 
| 
      
 51 
     | 
    
         
            +
              def self.add_tag(tag, public_ids = [], options = {})
         
     | 
| 
      
 52 
     | 
    
         
            +
                exclusive = options.delete(:exclusive)
         
     | 
| 
      
 53 
     | 
    
         
            +
                command = exclusive ? "set_exclusive" : "add"
         
     | 
| 
      
 54 
     | 
    
         
            +
                return self.call_tags_api(tag, command, public_ids, options)    
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              def self.remove_tag(tag, public_ids = [], options = {})
         
     | 
| 
      
 58 
     | 
    
         
            +
                return self.call_tags_api(tag, "remove", public_ids, options)    
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              def self.replace_tag(tag, public_ids = [], options = {})
         
     | 
| 
      
 62 
     | 
    
         
            +
                return self.call_tags_api(tag, "replace", public_ids, options)    
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
              
         
     | 
| 
      
 65 
     | 
    
         
            +
              private
         
     | 
| 
      
 66 
     | 
    
         
            +
              
         
     | 
| 
      
 67 
     | 
    
         
            +
              def self.call_tags_api(tag, command, public_ids = [], options = {})
         
     | 
| 
      
 68 
     | 
    
         
            +
                return call_api("tags", options) do
         
     | 
| 
      
 69 
     | 
    
         
            +
                  {
         
     | 
| 
      
 70 
     | 
    
         
            +
                    :timestamp=>Time.now.to_i,
         
     | 
| 
      
 71 
     | 
    
         
            +
                    :tag=>tag,
         
     | 
| 
      
 72 
     | 
    
         
            +
                    :public_ids => Array(public_ids),
         
     | 
| 
      
 73 
     | 
    
         
            +
                    :command => command
         
     | 
| 
      
 74 
     | 
    
         
            +
                  }    
         
     | 
| 
      
 75 
     | 
    
         
            +
                end    
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
                 
         
     | 
| 
      
 78 
     | 
    
         
            +
              def self.call_api(action, options)
         
     | 
| 
      
 79 
     | 
    
         
            +
                options = options.clone
         
     | 
| 
      
 80 
     | 
    
         
            +
                return_error = options.delete(:return_error)
         
     | 
| 
      
 81 
     | 
    
         
            +
                api_key = options[:api_key] || Cloudinary.config.api_key || raise("Must supply api_key")
         
     | 
| 
      
 82 
     | 
    
         
            +
                api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise("Must supply api_secret")
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                params, non_signable = yield
         
     | 
| 
      
 85 
     | 
    
         
            +
                non_signable ||= []
         
     | 
| 
      
 86 
     | 
    
         
            +
                
         
     | 
| 
      
 87 
     | 
    
         
            +
                params[:signature] = Cloudinary::Utils.api_sign_request(params.reject{|k,v| non_signable.include?(k)}, api_secret)
         
     | 
| 
      
 88 
     | 
    
         
            +
                params[:api_key] = api_key
         
     | 
| 
      
 89 
     | 
    
         
            +
                cloudinary = options.delete(:upload_prefix) || Cloudinary.config.upload_prefix || "https://api.cloudinary.com"
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                resource_type = options.delete(:resource_type) || "image"
         
     | 
| 
      
 92 
     | 
    
         
            +
                result = nil
         
     | 
| 
      
 93 
     | 
    
         
            +
                cloud_name = Cloudinary.config.cloud_name || raise("Must supply cloud_name")
         
     | 
| 
      
 94 
     | 
    
         
            +
                RestClient::Request.execute(:method => :post, :url => "#{cloudinary}/v1_1/#{cloud_name}/#{resource_type}/#{action}", :payload => params, :timeout=>60) do
         
     | 
| 
      
 95 
     | 
    
         
            +
                  |response, request, tmpresult|
         
     | 
| 
      
 96 
     | 
    
         
            +
                  raise "Server returned unexpected status code - #{response.code} - #{response.body}" if ![200,400,500].include?(response.code)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 98 
     | 
    
         
            +
                    result = Cloudinary::Utils.json_decode(response.body)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  rescue => e
         
     | 
| 
      
 100 
     | 
    
         
            +
                    # Error is parsing json
         
     | 
| 
      
 101 
     | 
    
         
            +
                    raise "Error parsing server response (#{response.code}) - #{response.body}. Got - #{e}"
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
                  if result["error"]
         
     | 
| 
      
 104 
     | 
    
         
            +
                    if return_error
         
     | 
| 
      
 105 
     | 
    
         
            +
                      result["error"]["http_code"] = response.code
         
     | 
| 
      
 106 
     | 
    
         
            +
                    else
         
     | 
| 
      
 107 
     | 
    
         
            +
                      raise result["error"]["message"]
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end        
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
                
         
     | 
| 
      
 112 
     | 
    
         
            +
                result    
         
     | 
| 
      
 113 
     | 
    
         
            +
              end
         
     | 
| 
      
 114 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,98 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright Cloudinary
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'digest/sha1'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Cloudinary::Utils
         
     | 
| 
      
 5 
     | 
    
         
            +
              SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"
         
     | 
| 
      
 6 
     | 
    
         
            +
              
         
     | 
| 
      
 7 
     | 
    
         
            +
              # Warning: options are being destructively updated!
         
     | 
| 
      
 8 
     | 
    
         
            +
              def self.generate_transformation_string(options={})
         
     | 
| 
      
 9 
     | 
    
         
            +
                width = options[:width]
         
     | 
| 
      
 10 
     | 
    
         
            +
                height = options[:height]
         
     | 
| 
      
 11 
     | 
    
         
            +
                size = options.delete(:size)
         
     | 
| 
      
 12 
     | 
    
         
            +
                width, height = size.split("x") if size    
         
     | 
| 
      
 13 
     | 
    
         
            +
                options.delete(:width) if width && width < 1 
         
     | 
| 
      
 14 
     | 
    
         
            +
                options.delete(:height) if height && height < 1    
         
     | 
| 
      
 15 
     | 
    
         
            +
                 
         
     | 
| 
      
 16 
     | 
    
         
            +
                crop = options.delete(:crop)
         
     | 
| 
      
 17 
     | 
    
         
            +
                width=height=nil if crop.nil?
         
     | 
| 
      
 18 
     | 
    
         
            +
                
         
     | 
| 
      
 19 
     | 
    
         
            +
                gravity = options.delete(:gravity)
         
     | 
| 
      
 20 
     | 
    
         
            +
                quality = options.delete(:quality)
         
     | 
| 
      
 21 
     | 
    
         
            +
                named_transformation = Array(options.delete(:transformation)).join(".")
         
     | 
| 
      
 22 
     | 
    
         
            +
                prefix = options.delete(:prefix)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                params = {:w=>width, :h=>height, :t=>named_transformation, :c=>crop, :q=>quality, :g=>gravity, :p=>prefix}
         
     | 
| 
      
 25 
     | 
    
         
            +
                transformation = params.reject{|k,v| v.blank?}.map{|k,v| [k.to_s, v]}.sort_by(&:first).map{|k,v| "#{k}_#{v}"}.join(",")
         
     | 
| 
      
 26 
     | 
    
         
            +
                raw_transformation = options.delete(:raw_transformation)
         
     | 
| 
      
 27 
     | 
    
         
            +
                transformation = [transformation, raw_transformation].reject(&:blank?).join(",")
         
     | 
| 
      
 28 
     | 
    
         
            +
                transformation    
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
              
         
     | 
| 
      
 31 
     | 
    
         
            +
              def self.api_sign_request(params_to_sign, api_secret)
         
     | 
| 
      
 32 
     | 
    
         
            +
                to_sign = params_to_sign.reject{|k,v| v.blank?}.map{|k,v| [k.to_s, v.is_a?(Array) ? v.join(",") : v]}.sort_by(&:first).map{|k,v| "#{k}=#{v}"}.join("&")
         
     | 
| 
      
 33 
     | 
    
         
            +
                Digest::SHA1.hexdigest("#{to_sign}#{api_secret}")
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              # Warning: options are being destructively updated!
         
     | 
| 
      
 37 
     | 
    
         
            +
              def self.cloudinary_url(source, options = {})
         
     | 
| 
      
 38 
     | 
    
         
            +
                transformation = self.generate_transformation_string(options)
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                type = options.delete(:type) || :upload
         
     | 
| 
      
 41 
     | 
    
         
            +
                resource_type = options.delete(:resource_type) || "image"
         
     | 
| 
      
 42 
     | 
    
         
            +
                version = options.delete(:version)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                format = options.delete(:format)
         
     | 
| 
      
 45 
     | 
    
         
            +
                source = "#{source}.#{format}" if format
         
     | 
| 
      
 46 
     | 
    
         
            +
                
         
     | 
| 
      
 47 
     | 
    
         
            +
                # Configuration options
         
     | 
| 
      
 48 
     | 
    
         
            +
                # newsodrome.cloudinary.com, images.newsodrome.com, cloudinary.com/res/newsodrome, a9fj209daf.cloudfront.net
         
     | 
| 
      
 49 
     | 
    
         
            +
                cloud_name = options.delete(:cloud_name) || Cloudinary.config.cloud_name || raise("Must supply cloud_name in tag or in configuration")
         
     | 
| 
      
 50 
     | 
    
         
            +
                
         
     | 
| 
      
 51 
     | 
    
         
            +
                if cloud_name.start_with?("/")
         
     | 
| 
      
 52 
     | 
    
         
            +
                  prefix = "/res" + cloud_name
         
     | 
| 
      
 53 
     | 
    
         
            +
                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 
     | 
    
         
            +
                  if secure && secure_distribution.nil?
         
     | 
| 
      
 58 
     | 
    
         
            +
                    if private_cdn
         
     | 
| 
      
 59 
     | 
    
         
            +
                      raise "secure_distribution not defined"
         
     | 
| 
      
 60 
     | 
    
         
            +
                    else
         
     | 
| 
      
 61 
     | 
    
         
            +
                      secure_distribution = SHARED_CDN 
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                  
         
     | 
| 
      
 65 
     | 
    
         
            +
                  if secure
         
     | 
| 
      
 66 
     | 
    
         
            +
                    prefix = "https://#{secure_distribution}"
         
     | 
| 
      
 67 
     | 
    
         
            +
                  else
         
     | 
| 
      
 68 
     | 
    
         
            +
                    prefix = "http://#{private_cdn ? "#{cloud_name}-" : ""}res.cloudinary.com"
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end    
         
     | 
| 
      
 70 
     | 
    
         
            +
                  prefix += "/#{cloud_name}" if !private_cdn
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
                
         
     | 
| 
      
 73 
     | 
    
         
            +
                source = prefix + "/" + [resource_type, 
         
     | 
| 
      
 74 
     | 
    
         
            +
                 type, transformation, version ? "v#{version}" : nil,
         
     | 
| 
      
 75 
     | 
    
         
            +
                 source].reject(&:blank?).join("/").gsub("//", "/")
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
              
         
     | 
| 
      
 78 
     | 
    
         
            +
              def self.random_public_id
         
     | 
| 
      
 79 
     | 
    
         
            +
                (defined?(ActiveSupport::SecureRandom) ? ActiveSupport::SecureRandom : SecureRandom).base64(16).downcase.gsub(/[^a-z0-9]/, "")    
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
              
         
     | 
| 
      
 82 
     | 
    
         
            +
              @@json_decode = false
         
     | 
| 
      
 83 
     | 
    
         
            +
              def self.json_decode(str)
         
     | 
| 
      
 84 
     | 
    
         
            +
                if !@@json_decode
         
     | 
| 
      
 85 
     | 
    
         
            +
                  @@json_decode = true
         
     | 
| 
      
 86 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 87 
     | 
    
         
            +
                    require 'json'
         
     | 
| 
      
 88 
     | 
    
         
            +
                  rescue LoadError
         
     | 
| 
      
 89 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 90 
     | 
    
         
            +
                      require 'active_support/json'
         
     | 
| 
      
 91 
     | 
    
         
            +
                    rescue LoadError
         
     | 
| 
      
 92 
     | 
    
         
            +
                      raise "Please add the json gem or active_support to your Gemfile"            
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
                defined?(JSON) ? JSON.parse(str) : ActiveSupport::JSON.decode(str)
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification 
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: cloudinary
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version 
         
     | 
| 
      
 4 
     | 
    
         
            +
              prerelease: 
         
     | 
| 
      
 5 
     | 
    
         
            +
              version: 1.0.0
         
     | 
| 
      
 6 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 7 
     | 
    
         
            +
            authors: 
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Nadav Soferman
         
     | 
| 
      
 9 
     | 
    
         
            +
            - Itai Lahan
         
     | 
| 
      
 10 
     | 
    
         
            +
            - Tal Lev-Ami
         
     | 
| 
      
 11 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 12 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 13 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            date: 2012-02-22 00:00:00 +02:00
         
     | 
| 
      
 16 
     | 
    
         
            +
            default_executable: 
         
     | 
| 
      
 17 
     | 
    
         
            +
            dependencies: 
         
     | 
| 
      
 18 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 19 
     | 
    
         
            +
              name: rest-client
         
     | 
| 
      
 20 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 21 
     | 
    
         
            +
              requirement: &id001 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 22 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: "0"
         
     | 
| 
      
 27 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 28 
     | 
    
         
            +
              version_requirements: *id001
         
     | 
| 
      
 29 
     | 
    
         
            +
            description: Client library for easily using the Cloudinary service
         
     | 
| 
      
 30 
     | 
    
         
            +
            email: 
         
     | 
| 
      
 31 
     | 
    
         
            +
            - nadav.soferman@cloudinary.com
         
     | 
| 
      
 32 
     | 
    
         
            +
            - itai.lahan@cloudinary.com
         
     | 
| 
      
 33 
     | 
    
         
            +
            - tal.levami@cloudinary.com
         
     | 
| 
      
 34 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            files: 
         
     | 
| 
      
 41 
     | 
    
         
            +
            - .gitignore
         
     | 
| 
      
 42 
     | 
    
         
            +
            - Gemfile
         
     | 
| 
      
 43 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 44 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 45 
     | 
    
         
            +
            - cloudinary.gemspec
         
     | 
| 
      
 46 
     | 
    
         
            +
            - lib/cloudinary.rb
         
     | 
| 
      
 47 
     | 
    
         
            +
            - lib/cloudinary/blob.rb
         
     | 
| 
      
 48 
     | 
    
         
            +
            - lib/cloudinary/carrier_wave.rb
         
     | 
| 
      
 49 
     | 
    
         
            +
            - lib/cloudinary/downloader.rb
         
     | 
| 
      
 50 
     | 
    
         
            +
            - lib/cloudinary/helper.rb
         
     | 
| 
      
 51 
     | 
    
         
            +
            - lib/cloudinary/helpers.rb
         
     | 
| 
      
 52 
     | 
    
         
            +
            - lib/cloudinary/migrator.rb
         
     | 
| 
      
 53 
     | 
    
         
            +
            - lib/cloudinary/uploader.rb
         
     | 
| 
      
 54 
     | 
    
         
            +
            - lib/cloudinary/utils.rb
         
     | 
| 
      
 55 
     | 
    
         
            +
            - lib/cloudinary/version.rb
         
     | 
| 
      
 56 
     | 
    
         
            +
            has_rdoc: true
         
     | 
| 
      
 57 
     | 
    
         
            +
            homepage: http://cloudinary.com
         
     | 
| 
      
 58 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 61 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            require_paths: 
         
     | 
| 
      
 64 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 65 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 66 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 67 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 68 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 69 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 70 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 71 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 72 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 73 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 74 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 75 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 76 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 77 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
            rubyforge_project: cloudinary
         
     | 
| 
      
 80 
     | 
    
         
            +
            rubygems_version: 1.6.2
         
     | 
| 
      
 81 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 82 
     | 
    
         
            +
            specification_version: 3
         
     | 
| 
      
 83 
     | 
    
         
            +
            summary: Client library for easily using the Cloudinary service
         
     | 
| 
      
 84 
     | 
    
         
            +
            test_files: []
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     |