From 2b997e70c221a05e20630995037887239e4ad055 Mon Sep 17 00:00:00 2001
From: Raphael Sofaer <raphael@joindiaspora.com>
Date: Mon, 21 Mar 2011 16:00:17 -0700
Subject: [PATCH] Refactor image processing to use 2 uploaders.  Federation and
 s3 need testing

---
 app/models/jobs/process_photo.rb              |  4 +-
 app/models/photo.rb                           | 36 +++++++----
 app/models/user.rb                            |  5 +-
 app/uploaders/image_uploader.rb               | 54 ----------------
 app/uploaders/processed_image.rb              | 33 ++++++++++
 app/uploaders/unprocessed_image.rb            | 20 ++++++
 app/views/photos/edit.html.haml               |  2 +-
 config/environments/production.rb             |  2 +-
 ...110321205715_unprocessed_image_uploader.rb | 14 +++++
 db/schema.rb                                  |  6 +-
 spec/models/jobs/process_photo_spec.rb        |  5 +-
 spec/models/photo_spec.rb                     | 62 +++++++++++--------
 spec/models/user_spec.rb                      |  4 ++
 spec/spec_helper.rb                           |  2 +-
 14 files changed, 144 insertions(+), 105 deletions(-)
 delete mode 100644 app/uploaders/image_uploader.rb
 create mode 100644 app/uploaders/processed_image.rb
 create mode 100644 app/uploaders/unprocessed_image.rb
 create mode 100644 db/migrate/20110321205715_unprocessed_image_uploader.rb

diff --git a/app/models/jobs/process_photo.rb b/app/models/jobs/process_photo.rb
index 2c42f3b934..fb85368b82 100644
--- a/app/models/jobs/process_photo.rb
+++ b/app/models/jobs/process_photo.rb
@@ -4,10 +4,10 @@
 
 
 module Job
-  class ProcessPhoto < Base 
+  class ProcessPhoto < Base
     @queue = :photos
     def self.perform_delegate(photo_id)
-        Photo.find(photo_id).image.post_process
+      Photo.find(photo_id).process
     end
   end
 end
diff --git a/app/models/photo.rb b/app/models/photo.rb
index fb6bd526a5..0cfb47b132 100644
--- a/app/models/photo.rb
+++ b/app/models/photo.rb
@@ -4,7 +4,8 @@
 
 class Photo < Post
   require 'carrierwave/orm/activerecord'
-  mount_uploader :image, ImageUploader
+  mount_uploader :processed_image, ProcessedImage
+  mount_uploader :unprocessed_image, UnprocessedImage
 
   xml_attr :remote_photo_path
   xml_attr :remote_photo_name
@@ -33,23 +34,25 @@ class Photo < Post
     photo = super(params)
     image_file = params.delete(:user_file)
     photo.random_string = gen_random_string(10)
-    photo.processed = false
-    photo.image.store! image_file
-    photo.update_photo_remote_path
+    photo.unprocessed_image.store! image_file
     photo
   end
 
   def not_processed?
-    !self.processed
+    processed_image.path.nil?
   end
 
-  def update_photo_remote_path
-    unless self.image.url.match(/^https?:\/\//)
+  def processed?
+    !processed_image.path.nil?
+  end
+
+  def update_remote_path
+    unless self.processed_image.url.match(/^https?:\/\//)
       pod_url = AppConfig[:pod_url].dup
       pod_url.chop! if AppConfig[:pod_url][-1,1] == '/'
-      remote_path = "#{pod_url}#{self.image.url}"
+      remote_path = "#{pod_url}#{self.processed_image.url}"
     else
-      remote_path = self.image.url
+      remote_path = self.processed_image.url
     end
 
     name_start = remote_path.rindex '/'
@@ -70,13 +73,13 @@ class Photo < Post
   end
 
   def url(name = nil)
-    if self.not_processed? || (!self.image.path.blank? && self.image.path.include?('.gif'))
-      image.url
-    elsif remote_photo_path
+    if remote_photo_path
       name = name.to_s + '_' if name
       remote_photo_path + name.to_s + remote_photo_name
+    elsif not_processed?
+      unprocessed_image.url
     else
-      image.url(name)
+      processed_image.url(name)
     end
   end
 
@@ -96,6 +99,13 @@ class Photo < Post
     Resque.enqueue(Job::ProcessPhoto, self.id)
   end
 
+  def process
+    return false if unprocessed_image.path.include?('.gif') || self.processed?
+    processed_image.store!(unprocessed_image) #Ultra naive
+    update_remote_path
+    save!
+  end
+
   def mutable?
     true
   end
diff --git a/app/models/user.rb b/app/models/user.rb
index ffcf0d2ce9..bf75d81a13 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -37,7 +37,7 @@ class User < ActiveRecord::Base
   has_many :contacts
   has_many :contact_people, :through => :contacts, :source => :person
   has_many :services
-  has_many :user_preferences  
+  has_many :user_preferences
 
   before_destroy :disconnect_everyone, :remove_mentions, :remove_person
   before_save do
@@ -52,7 +52,7 @@ class User < ActiveRecord::Base
       self.disable_mail = false
       self.save
     end
-    
+
     pref_hash.keys.each do |key|
       if pref_hash[key] == 'true'
         self.user_preferences.find_or_create_by_email_type(key)
@@ -226,6 +226,7 @@ class User < ActiveRecord::Base
   def update_profile(params)
     if photo = params.delete(:photo)
       photo.update_attributes(:pending => false) if photo.pending
+      photo.process
       params[:image_url] = photo.url(:thumb_large)
       params[:image_url_medium] = photo.url(:thumb_medium)
       params[:image_url_small] = photo.url(:thumb_small)
diff --git a/app/uploaders/image_uploader.rb b/app/uploaders/image_uploader.rb
deleted file mode 100644
index 827120ae69..0000000000
--- a/app/uploaders/image_uploader.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-#   Copyright (c) 2010, Diaspora Inc.  This file is
-#   licensed under the Affero General Public License version 3 or later.  See
-#   the COPYRIGHT file.
-
-class ImageUploader < CarrierWave::Uploader::Base
-  include CarrierWave::MiniMagick
-
-  def store_dir
-    "uploads/images"
-  end
-
-  def extension_white_list
-    %w(jpg jpeg png gif)
-  end
-
-  def filename
-    model.random_string + model.id.to_s + File.extname(@filename) if @filename
-  end
-
-  def post_process
-    self.send(:remove_versions!)
-    unless self.path.include?('.gif')
-      ImageUploader.instance_eval do
-        version :thumb_small do
-          process :resize_to_fill => [50,50]
-        end
-
-        version :thumb_medium do
-          process :resize_to_fill => [100,100]
-        end
-
-        version :thumb_large do
-          process :resize_to_fill => [300,300]
-        end
-
-        version :scaled_full do
-          process :resize_to_limit => [700,700]
-        end
-      end
-
-      self.recreate_versions!
-      self.model.processed = true
-      self.model.update_photo_remote_path
-      self.model.save
-    else
-      false
-    end
-  end
-
-  version :scaled_full
-  version :thumb_large
-  version :thumb_medium
-  version :thumb_small
-end
diff --git a/app/uploaders/processed_image.rb b/app/uploaders/processed_image.rb
new file mode 100644
index 0000000000..fb0478c80f
--- /dev/null
+++ b/app/uploaders/processed_image.rb
@@ -0,0 +1,33 @@
+#   Copyright (c) 2010, Diaspora Inc.  This file is
+#   licensed under the Affero General Public License version 3 or later.  See
+#   the COPYRIGHT file.
+
+class ProcessedImage < CarrierWave::Uploader::Base
+  include CarrierWave::MiniMagick
+
+  def store_dir
+    "uploads/images"
+  end
+
+  def extension_white_list
+    %w(jpg jpeg png gif)
+  end
+
+  def filename
+    model.random_string + model.id.to_s + File.extname(@filename) if @filename
+  end
+
+  version :thumb_small do
+    process :resize_to_fill => [50,50]
+  end
+  version :thumb_medium do
+    process :resize_to_fill => [100,100]
+  end
+  version :thumb_large do
+    process :resize_to_fill => [300,300]
+  end
+
+  version :scaled_full do
+    process :resize_to_limit => [700,700]
+  end
+end
diff --git a/app/uploaders/unprocessed_image.rb b/app/uploaders/unprocessed_image.rb
new file mode 100644
index 0000000000..ab43a3d070
--- /dev/null
+++ b/app/uploaders/unprocessed_image.rb
@@ -0,0 +1,20 @@
+#   Copyright (c) 2010, Diaspora Inc.  This file is
+#   licensed under the Affero General Public License version 3 or later.  See
+#   the COPYRIGHT file.
+
+class UnprocessedImage < CarrierWave::Uploader::Base
+  include CarrierWave::MiniMagick
+
+  def store_dir
+    "uploads/u_images"
+  end
+
+  def extension_white_list
+    %w(jpg jpeg png gif)
+  end
+
+  def filename
+    model.random_string + model.id.to_s + File.extname(@filename) if @filename
+  end
+
+end
diff --git a/app/views/photos/edit.html.haml b/app/views/photos/edit.html.haml
index 0128d72ad3..836e01c500 100644
--- a/app/views/photos/edit.html.haml
+++ b/app/views/photos/edit.html.haml
@@ -2,7 +2,7 @@
 -#   licensed under the Affero General Public License version 3 or later.  See
 -#   the COPYRIGHT file.
 
-%h2= "#{t('.editing')} #{@photo.image}"
+%h2= "#{t('.editing')} #{@photo.processed_image}"
 
 %div{:id => @photo.id}
   #show_photo
diff --git a/config/environments/production.rb b/config/environments/production.rb
index f02ea6132e..4de6630146 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -35,7 +35,7 @@ Diaspora::Application.configure do
 
   # Disable Rails's static asset server
   # In production, Apache or nginx will already do this
-  config.serve_static_assets = false
+  config.serve_static_assets = true
 
   # Enable serving of images, stylesheets, and javascripts from an asset server
   # config.action_controller.asset_host = "http://assets.example.com"
diff --git a/db/migrate/20110321205715_unprocessed_image_uploader.rb b/db/migrate/20110321205715_unprocessed_image_uploader.rb
new file mode 100644
index 0000000000..7b28886689
--- /dev/null
+++ b/db/migrate/20110321205715_unprocessed_image_uploader.rb
@@ -0,0 +1,14 @@
+require 'db/migrate/20110319005509_add_processed_to_post'
+class UnprocessedImageUploader < ActiveRecord::Migration
+  def self.up
+    AddProcessedToPost.down
+    rename_column :posts, :image, :processed_image
+    add_column :posts, :unprocessed_image, :string
+  end
+
+  def self.down
+    remove_column :posts, :unprocessed_image
+    rename_column :posts, :processed_image, :image
+    AddProcessedToPost.up
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f3032b399d..fd5a314f18 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20110319172136) do
+ActiveRecord::Schema.define(:version => 20110321205715) do
 
   create_table "aspect_memberships", :force => true do |t|
     t.integer  "aspect_id",  :null => false
@@ -220,12 +220,12 @@ ActiveRecord::Schema.define(:version => 20110319172136) do
     t.text     "remote_photo_path"
     t.string   "remote_photo_name"
     t.string   "random_string"
-    t.string   "image"
+    t.string   "processed_image"
     t.text     "youtube_titles"
     t.datetime "created_at"
     t.datetime "updated_at"
     t.string   "mongo_id"
-    t.boolean  "processed",         :default => true
+    t.string   "unprocessed_image"
   end
 
   add_index "posts", ["author_id"], :name => "index_posts_on_person_id"
diff --git a/spec/models/jobs/process_photo_spec.rb b/spec/models/jobs/process_photo_spec.rb
index 6dd4f9a998..d49db0ace9 100644
--- a/spec/models/jobs/process_photo_spec.rb
+++ b/spec/models/jobs/process_photo_spec.rb
@@ -1,10 +1,9 @@
 require 'spec_helper'
 
 describe Job::ProcessPhoto do
-  it 'calls post_process on an image uploader' do
+  it 'calls process on the photo' do
     photo = mock()
-    photo.should_receive(:image).and_return(photo)
-    photo.should_receive(:post_process)
+    photo.should_receive(:process)
     Photo.should_receive(:find).with(1).and_return(photo)
     Job::ProcessPhoto.perform(1)
   end
diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb
index 482937c273..d8219ddac8 100644
--- a/spec/models/photo_spec.rb
+++ b/spec/models/photo_spec.rb
@@ -14,9 +14,7 @@ describe Photo do
     @fail_fixture_name = File.join(File.dirname(__FILE__), '..', 'fixtures', 'msg.xml')
 
     @photo  = @user.build_post(:photo, :user_file=> File.open(@fixture_name), :to => @aspect.id)
-    @photo.processed = true
     @photo2 = @user.build_post(:photo, :user_file=> File.open(@fixture_name), :to => @aspect.id)
-    @photo2.processed = true 
   end
 
   describe "protected attributes" do
@@ -44,7 +42,7 @@ describe Photo do
       @photo.save!
     end
   end
-  
+
   it 'is mutable' do
     @photo.mutable?.should == true
   end
@@ -54,33 +52,47 @@ describe Photo do
   end
 
   describe '#diaspora_initialize' do
+    before do
+      image = File.open(@fixture_name)
+      @photo = Photo.diaspora_initialize(
+                :author => @user.person, :user_file => image)
+    end
     it 'sets the persons diaspora handle' do
       @photo2.diaspora_handle.should == @user.person.diaspora_handle
     end
-    it 'has a constructor' do
+    it 'builds the photo without saving' do
+      @photo.created_at.nil?.should be_true
+      @photo.unprocessed_image.read.nil?.should be_false
+    end
+  end
+
+  describe '#update_remote_path' do
+    before do
       image = File.open(@fixture_name)
-      photo = Photo.diaspora_initialize(
+      @photo = Photo.diaspora_initialize(
                 :author => @user.person, :user_file => image)
-      photo.created_at.nil?.should be_true
-      photo.image.read.nil?.should be_false
+      @photo.processed_image.store!(@photo.unprocessed_image)
+      @photo.save!
     end
     it 'sets a remote url' do
-      image = File.open(@fixture_name)
-      photo = Photo.diaspora_initialize(
-                :author => @user.person, :user_file => image)
-      photo.remote_photo_path.should include("http")
-      photo.remote_photo_name.should include(".png")
+      @photo.remote_photo_path.should be_nil
+      @photo.remote_photo_name.should be_nil
+
+      @photo.update_remote_path
+
+      @photo.remote_photo_path.should include("http")
+      @photo.remote_photo_name.should include(".png")
     end
   end
 
   it 'should save a photo' do
-    @photo.image.store! File.open(@fixture_name)
+    @photo.unprocessed_image.store! File.open(@fixture_name)
     @photo.save.should == true
     begin
-      binary = @photo.image.read.force_encoding('BINARY')
+      binary = @photo.unprocessed_image.read.force_encoding('BINARY')
       fixture_binary = File.open(@fixture_name).read.force_encoding('BINARY')
     rescue NoMethodError # Ruby 1.8 doesn't have force_encoding
-      binary = @photo.image.read
+      binary = @photo.unprocessed_image.read
       fixture_binary = File.open(@fixture_name).read
     end
     binary.should == fixture_binary
@@ -88,7 +100,7 @@ describe Photo do
 
   context 'with a saved photo' do
     before do
-      @photo.image.store! File.open(@fixture_name)
+      @photo.unprocessed_image.store! File.open(@fixture_name)
     end
     it 'should have text' do
       @photo.text= "cool story, bro"
@@ -105,25 +117,25 @@ describe Photo do
     end
 
     it 'should not use the imported filename as the url' do
-      @photo.image.url.include?(@fixture_filename).should be false
-      @photo.image.url(:thumb_medium).include?("/" + @fixture_filename).should be false
+      @photo.url.include?(@fixture_filename).should be false
+      @photo.url(:thumb_medium).include?("/" + @fixture_filename).should be false
     end
   end
 
   describe 'non-image files' do
     it 'should not store' do
       file = File.open(@fail_fixture_name)
-      @photo.image.should_receive(:check_whitelist!)
       lambda {
-        @photo.image.store! file
-      }.should raise_error
+        @photo.unprocessed_image.store! file
+      }.should raise_error CarrierWave::IntegrityError, 'You are not allowed to upload "xml" files, allowed types: ["jpg", "jpeg", "png", "gif"]'
     end
 
   end
 
   describe 'serialization' do
     before do
-      @photo.image.store! File.open(@fixture_name)
+      @photo.process
+      @photo.save!
       @xml = @photo.to_xml.to_s
     end
     it 'serializes the url' do
@@ -134,11 +146,11 @@ describe Photo do
       @xml.include?(@user.diaspora_handle).should be true
     end
   end
-  
+
   describe 'remote photos' do
     it 'should set the remote_photo on marshalling' do
-      @photo.image.store! File.open(@fixture_name)
-      @photo.save
+      @photo.process
+      @photo.save!
       #security hax
       user2 = Factory.create(:user)
       aspect2 = user2.aspects.create(:name => "foobars")
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 95fd1d6bf6..a9daff2a96 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -300,6 +300,10 @@ describe User do
         alice.update_profile(@params).should be true
         @photo.reload.pending.should be_false
       end
+      it 'post-processes the photo' do
+        @photo.should_receive(:process)
+        alice.update_profile(@params).should be true
+      end
     end
   end
 
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 3a19f50f2c..399817e576 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -42,7 +42,7 @@ RSpec.configure do |config|
   end
 end
 
-ImageUploader.enable_processing = false
+ProcessedImage.enable_processing = false
 
 def set_up_friends
   local_luke = Factory(:user_with_aspect, :username => "luke")
-- 
GitLab