Skip to content

Commit

Permalink
getting there
Browse files Browse the repository at this point in the history
  • Loading branch information
will-r committed Apr 13, 2012
1 parent 0a4679b commit e3329e5
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 113 deletions.
107 changes: 0 additions & 107 deletions app/models/upload.rb

This file was deleted.

31 changes: 25 additions & 6 deletions lib/cropper/attachment.rb
Expand Up @@ -32,7 +32,9 @@ def has_upload(attachment_name=:image, options={})
end

options.reverse_merge!(:geometry => "640x960#", :cropped => true)
options[:geometry].sub!(/\D*$/, '#') if options[:cropped]
options[:geometry].sub!(/\D*$/, '') if options[:cropped]
# raise here if geometry is not useable

class_variable_set(:"@@#{attachment_name}_cropped", options[:cropped])

# The essential step is present in this style definition. It specifies the OffsetThumbnail processor,
Expand All @@ -46,15 +48,17 @@ def has_upload(attachment_name=:image, options={})

# The processor will first scale the image to the width that is specified by the scale_width property of the instance
:scale => lambda { |att|
width = send :"#{attachment_name}_scale_width"
STDERR.puts "scale #{att.inspect}"
width = att.instance.send :"#{attachment_name}_scale_width"
"#{width}x"
},

# ...then perform the crop described by the width, height, offset_top and offset_left properties of the instance.
:crop_and_offset => lambda { |att|
width, height = size.split('x')
left = send :"#{attachment_name}_offset_left"
top = send :"#{attachment_name}_offset_top"
STDERR.puts "crop_and_offset #{att.inspect}"
width, height = options[:geometry].split('x')
left = att.instance.send :"#{attachment_name}_offset_left"
top = att.instance.send :"#{attachment_name}_offset_top"
"%dx%d%+d%+d" % [width, height, -left, -top]
}
}
Expand All @@ -64,7 +68,7 @@ def has_upload(attachment_name=:image, options={})
# [uploads](/app/models/upload.html) are the raw image files uploaded by this person.
# They are held separately as the basis for repeatable (and shareable) image assignment.
#
belongs_to :"#{attachment_name}_upload"
belongs_to :"#{attachment_name}_upload", :class_name => "Upload"
before_save :"read_#{attachment_name}_upload"

### Attachment
Expand Down Expand Up @@ -93,6 +97,7 @@ def has_upload(attachment_name=:image, options={})
# and apply the current crop and scale values.
#
define_method :"read_#{attachment_name}_upload" do
STDERR.puts ">> read_#{attachment_name}_upload"
if self.send(:"reprocess_#{attachment_name}?") && upload = self.send(:"#{attachment_name}_upload")
self.send :"#{attachment_name}=", upload.file
end
Expand All @@ -103,14 +108,28 @@ def has_upload(attachment_name=:image, options={})
cols = [:upload_id]
cols += [:upload_id, :scale_width, :offset_top, :offset_left] if options[:cropped]
define_method :"reprocess_#{attachment_name}?" do
STDERR.puts ">> reprocess_#{attachment_name}?"
cols.any? {|col| send(:"#{attachment_name}_#{col}_changed?") }
end

# * [name]_cropped? returns true if the named attachment is cropped on assignment. It can be useful in a form partial.
#
define_method :"#{attachment_name}_cropped?" do
STDERR.puts ">> #{attachment_name}_cropped?"
!!class_variable_get(:"@@#{attachment_name}_cropped")
end





define_method :"#{attachment_name}_for_cropping" do
if upload = send(:"#{attachment_name}_upload")
# here we introduce a convention that might not stand up
STDERR.puts ">> #{attachment_name}_for_cropping"
upload.url(:"#{attachment_name}")
end
end

end
end
Expand Down
123 changes: 123 additions & 0 deletions lib/cropper/upload.rb
@@ -0,0 +1,123 @@
# This is a base class for your uploads. It defines all the essential methods and offers some hooks that
# you can override to set local style definitions. All you need is something like this:
#
# class Upload << Cropper::Upload
# def thumbnail_styles
# {:precrop => "500x500>"}
# end
#
# If you have models with very different image-crop outcomes you may want to define a different precrop
# style for each one.
#
module Cropper
class Upload < ActiveRecord::Base
belongs_to :person

has_attached_file :file,
:path => ":rails_root/public/system/:class/:attachment/:id/:style/:filename",
:url => "/system/:class/:attachment/:id/:style/:filename",
:processors => lambda { |att| att.instance.precrop_processors },
:styles => lambda { |att| att.instance.precrop_styles }

def precrop_styles
{
:icon => { :geometry => "40x40#" },
:precrop => { :geometry => "1600x1600>" },
}
end

def precrop_processors
[:thumbnail]
end

## Image dimensions
#
# We need to know dimensions of the precrop image in order to set up the cropping interface, so we
# examine the uploaded file before it is flushed.
#
after_post_process :read_dimensions

# *original_geometry* returns the discovered dimensions of the uploaded file as a paperclip geometry object.
#
def original_geometry
@original_geometry ||= Paperclip::Geometry.new(original_width, original_height)
end

# *geometry*, given a style name, returns the dimensions of the file if that style were applied. For
# speed we calculate this rather than reading the file, which might be in S3 or some other distant place.
#
# The logic is in [lib/paperclip/geometry_tranformation.rb](/lib/paperclip/geometry_tranformation.html),
# which is a ruby library that mimics the action of imagemagick's convert command.
#
def geometry(style_name='original')
# These calculations are all memoised.
@geometry ||= {}
begin
@geometry[style_name] ||= if style_name.to_s == 'original'
# If no style name is given, or it is 'original', we return the original discovered dimensions.
original_geometry
else
# Otherwise, we apply a mock transformation to see what dimensions would result.
style = self.file.styles[style_name.to_sym]
original_geometry.transformed_by(style.geometry)
end
rescue Paperclip::TransformationError => e
# In case of explosion, we always return the original dimensions so that action can continue.
original_geometry
end
end

# *width* returns the width of this image in a given style.
#
def width(style_name='original')
geometry(style_name).width.to_i
end

# *height* returns the height of this image in a given style.
#
def height(style_name='original')
geometry(style_name).height.to_i
end

# *square?* returns true if width and height are the same.
#
def square?(style_name='original')
geometry(style_name).square?
end

# *vertical?* returns true if the image, in the given style, is taller than it is wide.
#
def vertical?(style_name='original')
geometry(style_name).vertical?
end

# *horizontal?* returns true if the image, in the given style, is wider than it is tall.
#
def horizontal?(style_name='original')
geometry(style_name).horizontal?
end

# *dimensions_known?* returns true we have managed to discover the dimensions of the original file.
#
def dimensions_known?
original_width? && original_height?
end

private

# *read_dimensions* is called after post processing to record in the database the original width, height
# and extension of the uploaded file. At this point the file queue will not have been flushed but the upload
# should be in place. We grab dimensions from the temp file and calculate thumbnail dimensions later, on demand.
#
def read_dimensions
if file = self.file.queued_for_write[:original]
geometry = Paperclip::Geometry.from_file(file)
self.original_width = geometry.width
self.original_height = geometry.height
self.original_extension = File.extname(file.path)
end
true
end

end
end
34 changes: 34 additions & 0 deletions lib/cropper/uploads_controller.rb
@@ -0,0 +1,34 @@
class UploadsController < ApplicationController
respond_to :js

def show
@upload = params[:id] == 'latest' || params[:id].blank? ? current_user.last_upload : Upload.find(params[:id])
respond_with(@upload)
end

def create
@upload = Upload.create(params[:upload])
render :partial => 'crop'
end

def edit
@upload = Upload.find(params[:id])
if @person && @upload = @person.upload
render :partial => 'crop', :locals => {
:scale_w => @person.image_scale_width,
:scale_h => @person.image_scale_height,
:scale_t => @person.image_offset_top,
:scale_l => @person.image_offset_left
}
else
render :partial => 'crop'
end
end

def destroy
@upload = Upload.find(params[:id])
@upload.destroy
respond_with(@upload)
end

end

0 comments on commit e3329e5

Please sign in to comment.