upload_juicer 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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
+