upload_juicer 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md ADDED
File without changes
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Juicer LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # Welcome!
2
+
3
+ This is a rather simple gem for working the the [Juicer
4
+ API](http://www.uploadjuicer.com) for resizing images. It's currently pretty
5
+ rough around the edges (no tests! ack!) but it works. :)
6
+
7
+ # The basics
8
+
9
+ ## Submitting jobs
10
+
11
+ The Job class provides two class-level methods for you to interact with the
12
+ API. First, the `submit` method takes an API key, a source URL, and a array
13
+ of output hashes. This is POSTed to the API as JSON and a hash is returned to
14
+ you with the results of the API call. You'll get back a job id, the source URL
15
+ and the outputs you defined, and an indication of the status ("queued" or
16
+ "failed").
17
+
18
+ Here's an example request to create a job:
19
+
20
+ UploadJuicer::Job.submit("your api key",
21
+ "http://farm3.static.flickr.com/2084/2222523486_5e1894e314.jpg",
22
+ [{"size" => "100x100>"}])
23
+
24
+ => {"id" => "a string", "outputs" => [{"size" => "100x100>"}], "status" => "queued"}
25
+
26
+ You can specify your own destination URLs as S3 locations:
27
+
28
+ UploadJuicer::Job.submit("your api key",
29
+ "http://farm3.static.flickr.com/2084/2222523486_5e1894e314.jpg",
30
+ [{"size" => "100x100>", "url" => "s3://mybucket/path/to/destfile.jpg"}])
31
+
32
+ => {"id" => "a string", "outputs" => [{"size" => "100x100>", "url" => "s3://mybucket/path/to/destfile.jpg"}], "status" => "queued"}
33
+
34
+ When you specify output URLs, those URLs show up in the hash returned when
35
+ queuing the job. For this to work, you'll need to grant access to Juicer to
36
+ write to your bucket. You can find Juicer's S3 ID in the documentation at the
37
+ Juicer site.
38
+
39
+ Though size is the only required key in the outputs hashes, you can add other
40
+ keys (e.g., a label, or your own ids) to the hashes and they will be passed
41
+ back to you. This could be useful if you aren't specifying output URLs and you
42
+ want to associate some data with the URLs you'll get back.
43
+
44
+ ## Querying jobs
45
+
46
+ You can use the id that was returned to you as the second argument (after your
47
+ API key) to the `info` method. The return hash would look exactly like the
48
+ hash you got back from `submit`, but hopefully the status will now be
49
+ "finished" rather than "queued" (or "failed").
50
+
51
+ # Using Juicer with Rails 3
52
+
53
+ The intended usage is to have your users upload files directly to S3 via
54
+ swfupload, create an UploadJuicer::Upload record via ajax once the upload is
55
+ complete, and then associate that UploadJuicer::Upload record and call the
56
+ Juicer API when the record your using is working with (e.g., a Project or a
57
+ Contact) is created or updated.
58
+
59
+ A Rails Engine and a generator is included with this gem to make it easy to
60
+ integrate Juicer with your Rails application. The engine provides an
61
+ UploadJuicer::Upload model, an UploadJuicer::Uploads controller, and a helper
62
+ to use in your app. The generator creates a migration, creates a configuration
63
+ file, and copies swfupload in place for you to use in your forms.
64
+
65
+ To use the generator, either specify your API key that you got from the Juicer
66
+ site with the --api-key option, or you specify that your API key will be
67
+ loaded in your Heroku environment (if you are using Juicer as a Heroku add-on)
68
+ with the --heroku option. After running the generator you'll need to run the
69
+ migration and edit the config/upload\_juicer.yml file to add your S3
70
+ credentials and specify which bucket you want to use for your uploads.
71
+
72
+ For swfupload to be able to upload to your bucket, you'll need to upload a
73
+ crossdomain.xml to the top-level of your bucket. A sample crossdomain.xml file
74
+ is placed in your public directory by the generator.
75
+
76
+ ## Example
77
+
78
+ Once you are all set up, here's an example of how you'd use the gem in a Rails
79
+ view and model:
80
+
81
+ ### app/views/people/new.html.erb
82
+
83
+ <%= form_for @person do |p| %>
84
+ <%= p.hidden_field :image_key, :id => :image_key %>
85
+ <%= p.hidden_field :image_id, :id => :image_id %>
86
+
87
+ <p>
88
+ <%= p.label :name %>
89
+ <%= p.text_field :name %>
90
+ </p>
91
+
92
+ <div id="file_container"></div>
93
+ <p id="upload_placeholder" class="uploadify"></p>
94
+
95
+ <p><%= p.submit %></p>
96
+ <% end %>
97
+
98
+ <% content_for :head do %>
99
+ <%= stylesheet_link_tag('swfupload.css') %>
100
+ <% end %>
101
+
102
+ <% content_for :foot do %>
103
+ <%= javascript_include_tag('swfupload.js', 'uploader.js') %>
104
+ <script type="text/javascript">
105
+ $('#upload_placeholder').uploadify(<%= swfupload_params %>);
106
+ </script>
107
+ <% end %>
108
+
109
+ This view just has two fields, the name and the file that is being uploaded.
110
+ the reference javascripts are copied into your public directory by the
111
+ generator. Once the file is selected, the upload begins (and the form is
112
+ disabled). Once the file is finished uploading, an ajax request is made to the
113
+ UploadJuicer::Uploads controller to create an UploadJuicer::Upload record, the
114
+ form is enabled, and the image\_key and image\_id fields are populated with
115
+ the JSON response from the UploadJuicer::Uploads controller with info about
116
+ the UploadJuicer::Upload model that was just created. This info is used when
117
+ the form is submitted to associated the UploadJuicer::Upload with the Person.
118
+ The Uploads helper provides the `swfupload_params` method, which does all the
119
+ request signing, etc. that S3 requires.
120
+
121
+ ### app/models/person.rb
122
+
123
+ class Person < ActiveRecord::Base
124
+ has_one :image, :class_name => 'UploadJuicer::Upload', :as => :uploadable
125
+
126
+ after_save :process_image
127
+
128
+ attr_accessor :image_key, :image_id
129
+
130
+ def process_image
131
+ return if @image_key.blank? || @image_id.blank?
132
+ if self.image = UploadJuicer::Upload.first(:conditions => { :key => @image_key, :id => @image_id, :uploadable_id => nil })
133
+ image.juice_upload(:avatar => { :size => '40x40>' }, :thumb => { :size => '100x100>' })
134
+ end
135
+ end
136
+ end
137
+
138
+ This model simply associates the UploadJuicer::Upload that was created via the
139
+ AJAX call, then calls the `juice_upload` method with a hash of labels and size
140
+ info to pass on to the Juicer API. The UploadJuicer::Upload model creates
141
+ partitioned paths for the files in your S3 bucket like so:
142
+
143
+ Original: http://s3.amazonaws.com/your_bucket/34j/e8r/9fu/file_name.jpg
144
+ Avatar: http://s3.amazonaws.com/your_bucket/34j/e8r/9fu/avatar/file_name.jpg
145
+ Thumb: http://s3.amazonaws.com/your_bucket/34j/e8r/9fu/thumb/file_name.jpg
146
+
147
+ ### app/views/person/show.html.erb
148
+
149
+ <h1><%= @person.name %></h1>
150
+ <p>Avatar: <%= image_tag(@person.image.url(:avatar)) %></p>
151
+ <p>Thumb: <%= image_tag(@person.image.url(:thumb)) %></p>
152
+ <p>Full image: <%= image_tag(@person.image.url) %></p>
153
+
154
+ This view shows how to get the publicly-readable S3 URLs from the
155
+ UploadJuicer::Upload record associated with the Person.
@@ -0,0 +1,8 @@
1
+ class UploadJuicer::UploadsController < ApplicationController
2
+ unloadable
3
+
4
+ def create
5
+ @upload = UploadJuicer::Upload.create(params.slice(:file_name, :size, :key))
6
+ render :json => @upload
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ module UploadsHelper
2
+ def s3_post_params(options = {})
3
+ acl = options[:acl] || 'public-read'
4
+ expiration = options[:expiration] || 6.hours.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z')
5
+ max_filesize = options[:max_filesize] || 2.gigabyte
6
+
7
+ policy = Base64.encode64(
8
+ { 'expiration' => expiration,
9
+ 'conditions' => [
10
+ {'bucket' => UploadJuicer::Config.s3['bucket']},
11
+ ['starts-with', '$key', s3_key],
12
+ {'acl' => acl},
13
+ {'success_action_status' => '201'},
14
+ ['starts-with', '$Filename', ''],
15
+ ['content-length-range', 0, max_filesize]
16
+ ]
17
+ }.to_json)
18
+
19
+ signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), UploadJuicer::Config.s3['secret_access_key'], policy)).gsub("\n", "")
20
+
21
+ {
22
+ "key" => "#{s3_key}/${filename}",
23
+ "AWSAccessKeyId" => "#{UploadJuicer::Config.s3['access_key_id']}",
24
+ "acl" => "#{acl}",
25
+ "policy" => "#{policy}",
26
+ "signature" => "#{signature}",
27
+ "success_action_status" => "201"
28
+ }
29
+ end
30
+
31
+ def s3_key
32
+ @s3_key ||= SecureRandom.hex(8).scan(/..../).join('/')
33
+ end
34
+
35
+ def s3_upload_url
36
+ @s3_upload_url ||= "http://#{UploadJuicer::Config.s3['bucket']}.s3.amazonaws.com/"
37
+ end
38
+
39
+ def swfupload_params
40
+ { :upload_url => s3_upload_url, :post_params => s3_post_params }.to_json.html_safe
41
+ end
42
+
43
+ end
@@ -0,0 +1,30 @@
1
+ require 'upload_juicer'
2
+
3
+ class UploadJuicer::Upload < ActiveRecord::Base
4
+ set_table_name 'upload_juicer_uploads'
5
+
6
+ belongs_to :uploadable, :polymorphic => :true
7
+
8
+ def url(style = nil)
9
+ "http://#{UploadJuicer::Config.s3['bucket']}.s3.amazonaws.com/#{s3_path(style)}"
10
+ end
11
+
12
+ def s3_url(style = nil)
13
+ "s3://#{UploadJuicer::Config.s3['bucket']}/#{s3_path(style)}"
14
+ end
15
+
16
+ def s3_path(style = nil)
17
+ style = style.to_s
18
+ style << '/' unless style.blank? || style.ends_with?('/')
19
+ "#{key}/#{style}#{file_name}"
20
+ end
21
+
22
+ # The outputs var goes from this:
23
+ # { :avatar => { :size => '40x40>' } }
24
+ # to this:
25
+ # [ { :label => 'avatar', :size => '40x40>', :url => s3_url('avatar') } ]
26
+ def juice_upload(outputs)
27
+ output_array = outputs.collect {|style, opts| { :label => style.to_s, :url => s3_url(style) }.merge(opts) }
28
+ UploadJuicer::Job.submit(UploadJuicer::Config.api_key, s3_url, output_array)
29
+ end
30
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do |map|
2
+ namespace :upload_juicer do
3
+ resources :uploads, :only => [ :create ]
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ UploadJuicer::Config.api_key = <%= api_key_fetcher %>
@@ -0,0 +1,20 @@
1
+ class CreateUploadTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :upload_juicer_uploads do |t|
4
+ t.string :file_name
5
+ t.string :key
6
+ t.integer :size
7
+ t.integer :uploadable_id
8
+ t.string :uploadable_type
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :upload_juicer_uploads, :key
14
+ add_index :upload_juicer_uploads, [:uploadable_id, :uploadable_type]
15
+ end
16
+
17
+ def self.down
18
+ drop_table :upload_juicer_uploads
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ class UploadJuicerGenerator < Rails::Generators::Base
2
+ include Rails::Generators::Migration
3
+
4
+ desc <<-EOD
5
+ This generator creates a configuration and migration to use the API at
6
+ http://uploadjuicer.com in your Rails application. If you signed up for
7
+ Juicer via Heroku, use the --heroku option to have the API key loaded
8
+ from your Heroku environment. Otherwise, use the --api_key option to
9
+ supply the API key you received after signing up at the Juicer site.
10
+
11
+ EOD
12
+
13
+ class_option :api_key, :type => :string, :desc => "Your Juicer API key"
14
+ class_option :heroku, :type => :boolean, :desc => "Use the Heroku addon to provide your Juicer API key"
15
+
16
+ def self.source_root
17
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
18
+ end
19
+
20
+ def self.next_migration_number(dirname)
21
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
22
+ end
23
+
24
+ def doit
25
+ if !options[:api_key] && !options[:heroku] && !File.exists?(File.join('config', 'upload_juicer.yml'))
26
+ puts "Please use --api-key or --heroku or create config/upload_juicer.yml"
27
+ exit
28
+ end
29
+ migration_template 'migration.rb', File.join('db', 'migrate', 'create_upload_tables.rb')
30
+ template 'config.yml', File.join('config', 'upload_juicer.yml')
31
+ directory 'public'
32
+ end
33
+
34
+ private
35
+
36
+ def api_key_fetcher
37
+ options[:api_key] ? options[:api_key] : "<%= ENV['JUICER_API_KEY'] %>"
38
+ end
39
+
40
+ end
@@ -0,0 +1,7 @@
1
+ module UploadJuicer
2
+ if defined?(::Rails::Engine)
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,8 @@
1
+ class String
2
+ # Backward compatibility for Rails 2.x
3
+ unless self.method_defined? :html_safe
4
+ def html_safe
5
+ self
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ module UploadJuicer
2
+ def self.rails_init
3
+ YAML::load(ERB.new(File.read(Rails.root.join('config', 'upload_juicer.yml'))).result)[Rails.env].each { |k, v| UploadJuicer::Config.send("#{k}=", v) } rescue nil
4
+ end
5
+
6
+ if defined?(::Rails::Railtie)
7
+ class Railtie < ::Rails::Railtie
8
+ config.after_initialize do |app|
9
+ UploadJuicer.rails_init
10
+ end
11
+ end
12
+ else
13
+ rails_init
14
+ end
15
+
16
+ end
@@ -0,0 +1,29 @@
1
+ require 'json'
2
+ require 'rest_client'
3
+ require 'ostruct'
4
+
5
+ module UploadJuicer
6
+ API_URL = 'http://app.uploadjuicer.com/jobs'
7
+ VERSION = '0.9.1'
8
+
9
+ Config = OpenStruct.new
10
+
11
+ class Job
12
+ def self.submit(api_key, url, outputs)
13
+ JSON.parse(RestClient.post("#{UploadJuicer::API_URL}?token=#{api_key}&gem=#{UploadJuicer::VERSION}",
14
+ { :url => url, :outputs => outputs }.to_json, { :content_type => :json, :accept => :json }))
15
+ end
16
+
17
+ def self.info(api_key, id)
18
+ JSON.parse(RestClient.get("#{UploadJuicer::API_URL}/#{id}?token=#{api_key}&gem=#{UploadJuicer::VERSION}",
19
+ { :content_type => :json, :accept => :json }))
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ if defined?(Rails)
26
+ require 'upload_juicer/extensions/string'
27
+ require 'upload_juicer/railtie'
28
+ require 'upload_juicer/engine'
29
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: upload_juicer
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 9
8
+ - 1
9
+ version: 0.9.1
10
+ platform: ruby
11
+ authors:
12
+ - Benjamin Curtis
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-20 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rest-client
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: json
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :runtime
43
+ version_requirements: *id002
44
+ description:
45
+ email:
46
+ - ben@uploadjuicer.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files: []
52
+
53
+ files:
54
+ - lib/generators/upload_juicer/templates/initializer.rb
55
+ - lib/generators/upload_juicer/templates/migration.rb
56
+ - lib/generators/upload_juicer/upload_juicer_generator.rb
57
+ - lib/upload_juicer/engine.rb
58
+ - lib/upload_juicer/extensions/string.rb
59
+ - lib/upload_juicer/railtie.rb
60
+ - lib/upload_juicer.rb
61
+ - app/controllers/upload_juicer/uploads_controller.rb
62
+ - app/helpers/uploads_helper.rb
63
+ - app/models/upload_juicer/upload.rb
64
+ - config/routes.rb
65
+ - LICENSE
66
+ - CHANGELOG.md
67
+ - README.md
68
+ has_rdoc: true
69
+ homepage: http://www.uploadjuicer.com
70
+ licenses: []
71
+
72
+ post_install_message:
73
+ rdoc_options: []
74
+
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ segments:
89
+ - 1
90
+ - 3
91
+ - 6
92
+ version: 1.3.6
93
+ requirements: []
94
+
95
+ rubyforge_project:
96
+ rubygems_version: 1.3.6
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: UploadJuicer juices up your images!
100
+ test_files: []
101
+