avatar 0.0.4 → 0.0.5
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/History.txt +10 -0
 - data/Manifest.txt +32 -6
 - data/lib/avatar.rb +1 -1
 - data/lib/avatar/source/gravatar_source.rb +7 -5
 - data/lib/avatar/source/paperclip_source.rb +52 -0
 - data/lib/avatar/source/wrapper.rb +6 -0
 - data/lib/avatar/source/wrapper/abstract_source_wrapper.rb +33 -0
 - data/lib/avatar/source/wrapper/rails_asset_source_wrapper.rb +36 -0
 - data/lib/avatar/source/wrapper/string_substitution_source_wrapper.rb +55 -0
 - data/lib/avatar/version.rb +1 -1
 - data/test/lib/paperclip/README +32 -0
 - data/test/lib/paperclip/Rakefile +41 -0
 - data/test/lib/paperclip/generators/paperclip/USAGE +5 -0
 - data/test/lib/paperclip/generators/paperclip/paperclip_generator.rb +27 -0
 - data/test/lib/paperclip/generators/paperclip/templates/paperclip_migration.rb +17 -0
 - data/test/lib/paperclip/init.rb +3 -0
 - data/test/lib/paperclip/lib/paperclip.rb +197 -0
 - data/test/lib/paperclip/lib/paperclip/attachment.rb +230 -0
 - data/test/lib/paperclip/lib/paperclip/geometry.rb +109 -0
 - data/test/lib/paperclip/lib/paperclip/iostream.rb +43 -0
 - data/test/lib/paperclip/lib/paperclip/thumbnail.rb +80 -0
 - data/test/lib/paperclip/lib/paperclip/upfile.rb +32 -0
 - data/test/lib/paperclip/tasks/paperclip_tasks.rake +38 -0
 - data/test/lib/paperclip/test/database.yml +5 -0
 - data/test/lib/paperclip/test/fixtures/12k.png +0 -0
 - data/test/lib/paperclip/test/fixtures/5k.png +0 -0
 - data/test/lib/paperclip/test/fixtures/bad.png +1 -0
 - data/test/lib/paperclip/test/helper.rb +38 -0
 - data/test/lib/paperclip/test/test_attachment.rb +99 -0
 - data/test/lib/paperclip/test/test_geometry.rb +142 -0
 - data/test/lib/paperclip/test/test_integration.rb +76 -0
 - data/test/lib/paperclip/test/test_iostream.rb +60 -0
 - data/test/lib/paperclip/test/test_paperclip.rb +83 -0
 - data/test/lib/paperclip/test/test_thumbnail.rb +76 -0
 - data/test/lib/schema.rb +14 -1
 - data/test/test_file_column_source.rb +8 -6
 - data/test/test_gravatar_source.rb +5 -0
 - data/test/test_helper.rb +7 -2
 - data/test/test_paperclip_source.rb +52 -0
 - data/test/test_rails_asset_source_wrapper.rb +37 -0
 - data/test/test_string_substitution_source_wrapper.rb +25 -0
 - data/website/index.html +1 -1
 - metadata +43 -11
 - data/lib/avatar/source/rails_asset_source.rb +0 -64
 - data/lib/avatar/source/string_substitution_source.rb +0 -45
 - data/lib/avatar/string_substitution.rb +0 -28
 - data/test/test_rails_asset_source.rb +0 -50
 - data/test/test_string_substitution.rb +0 -28
 - data/test/test_string_substitution_source.rb +0 -22
 
| 
         @@ -0,0 +1,43 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Provides method that can be included on File-type objects (IO, StringIO, Tempfile, etc) to allow stream copying
         
     | 
| 
      
 2 
     | 
    
         
            +
            # and Tempfile conversion.
         
     | 
| 
      
 3 
     | 
    
         
            +
            module IOStream
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              # Returns a Tempfile containing the contents of the readable object.
         
     | 
| 
      
 6 
     | 
    
         
            +
              def to_tempfile
         
     | 
| 
      
 7 
     | 
    
         
            +
                tempfile = Tempfile.new("stream")
         
     | 
| 
      
 8 
     | 
    
         
            +
                tempfile.binmode
         
     | 
| 
      
 9 
     | 
    
         
            +
                self.stream_to(tempfile)
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              # Copies one read-able object from one place to another in blocks, obviating the need to load
         
     | 
| 
      
 13 
     | 
    
         
            +
              # the whole thing into memory. Defaults to 8k blocks. If this module is included in both
         
     | 
| 
      
 14 
     | 
    
         
            +
              # both StringIO and Tempfile, then either can have its data copied anywhere else without typing
         
     | 
| 
      
 15 
     | 
    
         
            +
              # worries or memory overhead worries. Returns a File if a String is passed in as the destination
         
     | 
| 
      
 16 
     | 
    
         
            +
              # and returns the IO or Tempfile as passed in if one is sent as the destination.
         
     | 
| 
      
 17 
     | 
    
         
            +
              def stream_to path_or_file, in_blocks_of = 8192
         
     | 
| 
      
 18 
     | 
    
         
            +
                dstio = case path_or_file
         
     | 
| 
      
 19 
     | 
    
         
            +
                        when String   then File.new(path_or_file, "wb+")
         
     | 
| 
      
 20 
     | 
    
         
            +
                        when IO       then path_or_file
         
     | 
| 
      
 21 
     | 
    
         
            +
                        when Tempfile then path_or_file
         
     | 
| 
      
 22 
     | 
    
         
            +
                        end
         
     | 
| 
      
 23 
     | 
    
         
            +
                buffer = ""
         
     | 
| 
      
 24 
     | 
    
         
            +
                self.rewind
         
     | 
| 
      
 25 
     | 
    
         
            +
                while self.read(in_blocks_of, buffer) do
         
     | 
| 
      
 26 
     | 
    
         
            +
                  dstio.write(buffer)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
                dstio.rewind
         
     | 
| 
      
 29 
     | 
    
         
            +
                dstio
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            class IO
         
     | 
| 
      
 34 
     | 
    
         
            +
              include IOStream
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            %w( Tempfile StringIO ).each do |klass|
         
     | 
| 
      
 38 
     | 
    
         
            +
              if Object.const_defined? klass
         
     | 
| 
      
 39 
     | 
    
         
            +
                Object.const_get(klass).class_eval do
         
     | 
| 
      
 40 
     | 
    
         
            +
                  include IOStream
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,80 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Paperclip
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Handles thumbnailing images that are uploaded.
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Thumbnail
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :file, :current_geometry, :target_geometry, :format, :whiny_thumbnails
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                # Creates a Thumbnail object set to work on the +file+ given. It
         
     | 
| 
      
 8 
     | 
    
         
            +
                # will attempt to transform the image into one defined by +target_geometry+
         
     | 
| 
      
 9 
     | 
    
         
            +
                # which is a "WxH"-style string. +format+ will be inferred from the +file+
         
     | 
| 
      
 10 
     | 
    
         
            +
                # unless specified. Thumbnail creation will raise no errors unless
         
     | 
| 
      
 11 
     | 
    
         
            +
                # +whiny_thumbnails+ is true (which it is, by default.
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize file, target_geometry, format = nil, whiny_thumbnails = true
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @file             = file
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @crop             = target_geometry[-1,1] == '#'
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @target_geometry  = Geometry.parse target_geometry
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @current_geometry = Geometry.from_file file
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @whiny_thumbnails = whiny_thumbnails
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  @current_format   = File.extname(@file.path)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @basename         = File.basename(@file.path, @current_format)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @format = format
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # Creates a thumbnail, as specified in +initialize+, +make+s it, and returns the
         
     | 
| 
      
 26 
     | 
    
         
            +
                # resulting Tempfile.
         
     | 
| 
      
 27 
     | 
    
         
            +
                def self.make file, dimensions, format = nil, whiny_thumbnails = true
         
     | 
| 
      
 28 
     | 
    
         
            +
                  new(file, dimensions, format, whiny_thumbnails).make
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                # Returns true if the +target_geometry+ is meant to crop.
         
     | 
| 
      
 32 
     | 
    
         
            +
                def crop?
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @crop
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                # Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
         
     | 
| 
      
 37 
     | 
    
         
            +
                # that contains the new image.
         
     | 
| 
      
 38 
     | 
    
         
            +
                def make
         
     | 
| 
      
 39 
     | 
    
         
            +
                  src = @file
         
     | 
| 
      
 40 
     | 
    
         
            +
                  dst = Tempfile.new([@basename, @format].compact.join("."))
         
     | 
| 
      
 41 
     | 
    
         
            +
                  dst.binmode
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  command = <<-end_command
         
     | 
| 
      
 44 
     | 
    
         
            +
                    #{ Paperclip.path_for_command('convert') }
         
     | 
| 
      
 45 
     | 
    
         
            +
                    "#{ File.expand_path(src.path) }"
         
     | 
| 
      
 46 
     | 
    
         
            +
                    #{ transformation_command }
         
     | 
| 
      
 47 
     | 
    
         
            +
                    "#{ File.expand_path(dst.path) }"
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end_command
         
     | 
| 
      
 49 
     | 
    
         
            +
                  success = system(command.gsub(/\s+/, " "))
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  if success && $?.exitstatus != 0 && @whiny_thumbnails
         
     | 
| 
      
 52 
     | 
    
         
            +
                    raise PaperclipError, "There was an error processing this thumbnail"
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  dst
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                # Returns the command ImageMagick's +convert+ needs to transform the image
         
     | 
| 
      
 59 
     | 
    
         
            +
                # into the thumbnail.
         
     | 
| 
      
 60 
     | 
    
         
            +
                def transformation_command
         
     | 
| 
      
 61 
     | 
    
         
            +
                  scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  trans = "-scale \"#{scale}\""
         
     | 
| 
      
 63 
     | 
    
         
            +
                  trans << " -crop \"#{crop}\" +repage" if crop
         
     | 
| 
      
 64 
     | 
    
         
            +
                  trans
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              # Due to how ImageMagick handles its image format conversion and how Tempfile
         
     | 
| 
      
 69 
     | 
    
         
            +
              # handles its naming scheme, it is necessary to override how Tempfile makes
         
     | 
| 
      
 70 
     | 
    
         
            +
              # its names so as to allow for file extensions. Idea taken from the comments
         
     | 
| 
      
 71 
     | 
    
         
            +
              # on this blog post:
         
     | 
| 
      
 72 
     | 
    
         
            +
              # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
         
     | 
| 
      
 73 
     | 
    
         
            +
              class Tempfile < ::Tempfile
         
     | 
| 
      
 74 
     | 
    
         
            +
                # Replaces Tempfile's +make_tmpname+ with one that honors file extensions.
         
     | 
| 
      
 75 
     | 
    
         
            +
                def make_tmpname(basename, n)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  extension = File.extname(basename)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  sprintf("%s,%d,%d%s", File.basename(basename, extension), $$, n, extension)
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Paperclip
         
     | 
| 
      
 2 
     | 
    
         
            +
              # The Upfile module is a convenience module for adding uploaded-file-type methods
         
     | 
| 
      
 3 
     | 
    
         
            +
              # to the +File+ class. Useful for testing.
         
     | 
| 
      
 4 
     | 
    
         
            +
              #   user.avatar = File.new("test/test_avatar.jpg")
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Upfile
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                # Infer the MIME-type of the file from the extension.
         
     | 
| 
      
 8 
     | 
    
         
            +
                def content_type
         
     | 
| 
      
 9 
     | 
    
         
            +
                  type = self.path.match(/\.(\w+)$/)[1] rescue "octet-stream"
         
     | 
| 
      
 10 
     | 
    
         
            +
                  case type
         
     | 
| 
      
 11 
     | 
    
         
            +
                  when "jpg", "png", "gif" then "image/#{type}"
         
     | 
| 
      
 12 
     | 
    
         
            +
                  when "txt", "csv", "xml", "html", "htm", "css", "js" then "text/#{type}"
         
     | 
| 
      
 13 
     | 
    
         
            +
                  else "x-application/#{type}"
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # Returns the file's normal name.
         
     | 
| 
      
 18 
     | 
    
         
            +
                def original_filename
         
     | 
| 
      
 19 
     | 
    
         
            +
                  File.basename(self.path)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                # Returns the size of the file.
         
     | 
| 
      
 23 
     | 
    
         
            +
                def size
         
     | 
| 
      
 24 
     | 
    
         
            +
                  File.size(self)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            class File #:nodoc:
         
     | 
| 
      
 31 
     | 
    
         
            +
              include Paperclip::Upfile
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'environment'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            def obtain_class
         
     | 
| 
      
 4 
     | 
    
         
            +
              class_name = ENV['CLASS'] || ENV['class']
         
     | 
| 
      
 5 
     | 
    
         
            +
              @klass = Object.const_get(class_name)
         
     | 
| 
      
 6 
     | 
    
         
            +
            end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            def obtain_attachments
         
     | 
| 
      
 9 
     | 
    
         
            +
              name = ENV['ATTACHMENT'] || ENV['attachment']
         
     | 
| 
      
 10 
     | 
    
         
            +
              if !name.blank? && @klass.attachment_names.include?(name)
         
     | 
| 
      
 11 
     | 
    
         
            +
                [ name ]
         
     | 
| 
      
 12 
     | 
    
         
            +
              else
         
     | 
| 
      
 13 
     | 
    
         
            +
                @klass.attachment_definitions.keys
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            namespace :paperclip do
         
     | 
| 
      
 18 
     | 
    
         
            +
              desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT)"
         
     | 
| 
      
 19 
     | 
    
         
            +
              task :refresh do
         
     | 
| 
      
 20 
     | 
    
         
            +
                klass     = obtain_class
         
     | 
| 
      
 21 
     | 
    
         
            +
                instances = klass.find(:all)
         
     | 
| 
      
 22 
     | 
    
         
            +
                names     = obtain_attachments
         
     | 
| 
      
 23 
     | 
    
         
            +
                
         
     | 
| 
      
 24 
     | 
    
         
            +
                puts "Regenerating thumbnails for #{instances.length} instances of #{klass.name}:"
         
     | 
| 
      
 25 
     | 
    
         
            +
                instances.each do |instance|
         
     | 
| 
      
 26 
     | 
    
         
            +
                  names.each do |name|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    result = if instance.send("#{ name }?")
         
     | 
| 
      
 28 
     | 
    
         
            +
                      instance.send(name).send("post_process")
         
     | 
| 
      
 29 
     | 
    
         
            +
                      instance.send(name).save
         
     | 
| 
      
 30 
     | 
    
         
            +
                    else
         
     | 
| 
      
 31 
     | 
    
         
            +
                      true
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    print result ? "." : "x"; $stdout.flush
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
                puts " Done."
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         Binary file 
     | 
| 
         Binary file 
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            This is not an image.
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'test/unit'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'shoulda'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'mocha'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'tempfile'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            require 'active_record'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'ruby-debug'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            ROOT       = File.join(File.dirname(__FILE__), '..')
         
     | 
| 
      
 11 
     | 
    
         
            +
            RAILS_ROOT = ROOT
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            $LOAD_PATH << File.join(ROOT, 'lib')
         
     | 
| 
      
 14 
     | 
    
         
            +
            $LOAD_PATH << File.join(ROOT, 'lib', 'paperclip')
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            require File.join(ROOT, 'lib', 'paperclip.rb')
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures") 
         
     | 
| 
      
 19 
     | 
    
         
            +
            config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
         
     | 
| 
      
 20 
     | 
    
         
            +
            ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
         
     | 
| 
      
 21 
     | 
    
         
            +
            ActiveRecord::Base.establish_connection(config[ENV['RAILS_ENV'] || 'test'])
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            def rebuild_model options = {}
         
     | 
| 
      
 24 
     | 
    
         
            +
              ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
         
     | 
| 
      
 25 
     | 
    
         
            +
                table.column :other, :string
         
     | 
| 
      
 26 
     | 
    
         
            +
                table.column :avatar_file_name, :string
         
     | 
| 
      
 27 
     | 
    
         
            +
                table.column :avatar_content_type, :string
         
     | 
| 
      
 28 
     | 
    
         
            +
                table.column :avatar_file_size, :integer
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              ActiveRecord::Base.send(:include, Paperclip)
         
     | 
| 
      
 32 
     | 
    
         
            +
              Object.send(:remove_const, "Dummy") rescue nil
         
     | 
| 
      
 33 
     | 
    
         
            +
              Object.const_set("Dummy", Class.new(ActiveRecord::Base))
         
     | 
| 
      
 34 
     | 
    
         
            +
              Dummy.class_eval do
         
     | 
| 
      
 35 
     | 
    
         
            +
                include Paperclip
         
     | 
| 
      
 36 
     | 
    
         
            +
                has_attached_file :avatar, options
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,99 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'test/helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Dummy
         
     | 
| 
      
 4 
     | 
    
         
            +
              # This is a dummy class
         
     | 
| 
      
 5 
     | 
    
         
            +
            end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            class AttachmentTest < Test::Unit::TestCase
         
     | 
| 
      
 8 
     | 
    
         
            +
              context "An attachment" do
         
     | 
| 
      
 9 
     | 
    
         
            +
                setup do
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @default_options = {
         
     | 
| 
      
 11 
     | 
    
         
            +
                    :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
         
     | 
| 
      
 12 
     | 
    
         
            +
                  }
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @instance = stub
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @instance.stubs(:id).returns(41)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @instance.stubs(:class).returns(Dummy)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @instance.stubs(:[]).with(:test_file_name).returns("5k.png")
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @instance.stubs(:[]).with(:test_content_type).returns("image/png")
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @instance.stubs(:[]).with(:test_file_size).returns(12345)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @attachment = Paperclip::Attachment.new(:test, @instance, @default_options)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"))
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                should "return its default_url when no file assigned" do
         
     | 
| 
      
 24 
     | 
    
         
            +
                  assert @attachment.file.nil?
         
     | 
| 
      
 25 
     | 
    
         
            +
                  assert_equal "/tests/original/missing.png", @attachment.url
         
     | 
| 
      
 26 
     | 
    
         
            +
                  assert_equal "/tests/blah/missing.png", @attachment.url(:blah)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                context "when expecting three styles" do
         
     | 
| 
      
 30 
     | 
    
         
            +
                  setup do
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @attachment = Paperclip::Attachment.new(:test, @instance, @default_options.merge({
         
     | 
| 
      
 32 
     | 
    
         
            +
                      :styles => { :large  => ["400x400", :png],
         
     | 
| 
      
 33 
     | 
    
         
            +
                                   :medium => ["100x100", :gif],
         
     | 
| 
      
 34 
     | 
    
         
            +
                                   :small => ["32x32#", :jpg]}
         
     | 
| 
      
 35 
     | 
    
         
            +
                    }))
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  context "and assigned a file" do
         
     | 
| 
      
 39 
     | 
    
         
            +
                    setup do
         
     | 
| 
      
 40 
     | 
    
         
            +
                      @instance.expects(:[]=).with(:test_file_name, File.basename(@file.path))
         
     | 
| 
      
 41 
     | 
    
         
            +
                      @instance.expects(:[]=).with(:test_content_type, "image/png")
         
     | 
| 
      
 42 
     | 
    
         
            +
                      @instance.expects(:[]=).with(:test_file_size, @file.size)
         
     | 
| 
      
 43 
     | 
    
         
            +
                      @instance.expects(:[]=).with(:test_file_name, nil)
         
     | 
| 
      
 44 
     | 
    
         
            +
                      @instance.expects(:[]=).with(:test_content_type, nil)
         
     | 
| 
      
 45 
     | 
    
         
            +
                      @instance.expects(:[]=).with(:test_file_size, nil)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      @attachment.assign(@file)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    should "return the real url" do
         
     | 
| 
      
 50 
     | 
    
         
            +
                      assert @attachment.file
         
     | 
| 
      
 51 
     | 
    
         
            +
                      assert_equal "/tests/41/original/5k.png", @attachment.url
         
     | 
| 
      
 52 
     | 
    
         
            +
                      assert_equal "/tests/41/blah/5k.png", @attachment.url(:blah)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    should "be dirty" do
         
     | 
| 
      
 56 
     | 
    
         
            +
                      assert @attachment.dirty?
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    should "have its image and attachments as tempfiles" do
         
     | 
| 
      
 60 
     | 
    
         
            +
                      [nil, :large, :medium, :small].each do |style|
         
     | 
| 
      
 61 
     | 
    
         
            +
                        assert File.exists?(@attachment.to_io(style))
         
     | 
| 
      
 62 
     | 
    
         
            +
                      end
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    context "and saved" do
         
     | 
| 
      
 66 
     | 
    
         
            +
                      setup do
         
     | 
| 
      
 67 
     | 
    
         
            +
                        @attachment.save
         
     | 
| 
      
 68 
     | 
    
         
            +
                      end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                      should "commit the files to disk" do
         
     | 
| 
      
 71 
     | 
    
         
            +
                        [nil, :large, :medium, :small].each do |style|
         
     | 
| 
      
 72 
     | 
    
         
            +
                          io = @attachment.to_io(style)
         
     | 
| 
      
 73 
     | 
    
         
            +
                          assert File.exists?(io)
         
     | 
| 
      
 74 
     | 
    
         
            +
                          assert ! io.is_a?(::Tempfile)
         
     | 
| 
      
 75 
     | 
    
         
            +
                        end
         
     | 
| 
      
 76 
     | 
    
         
            +
                      end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                      should "save the files as the right formats and sizes" do
         
     | 
| 
      
 79 
     | 
    
         
            +
                        [[:large, 400, 61, "PNG"], [:medium, 100, 15, "GIF"], [:small, 32, 32, "JPEG"]].each do |style|
         
     | 
| 
      
 80 
     | 
    
         
            +
                          out = `identify -format "%w %h %b %m" #{@attachment.to_io(style.first).path}`
         
     | 
| 
      
 81 
     | 
    
         
            +
                          width, height, size, format = out.split(" ")
         
     | 
| 
      
 82 
     | 
    
         
            +
                          assert_equal style[1].to_s, width.to_s 
         
     | 
| 
      
 83 
     | 
    
         
            +
                          assert_equal style[2].to_s, height.to_s
         
     | 
| 
      
 84 
     | 
    
         
            +
                          assert_equal style[3].to_s, format.to_s
         
     | 
| 
      
 85 
     | 
    
         
            +
                        end
         
     | 
| 
      
 86 
     | 
    
         
            +
                      end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                      should "have #file be equal #to_io(:original)" do
         
     | 
| 
      
 89 
     | 
    
         
            +
                        assert @attachment.file == @attachment.to_io(:original)
         
     | 
| 
      
 90 
     | 
    
         
            +
                      end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                      should "still have its #file attribute not be nil" do
         
     | 
| 
      
 93 
     | 
    
         
            +
                        assert ! @attachment.file.nil?
         
     | 
| 
      
 94 
     | 
    
         
            +
                      end
         
     | 
| 
      
 95 
     | 
    
         
            +
                    end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,142 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'test/unit'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'shoulda'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), '..', 'lib', 'paperclip', 'geometry.rb')
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            class GeometryTest < Test::Unit::TestCase
         
     | 
| 
      
 8 
     | 
    
         
            +
              context "Paperclip::Geometry" do
         
     | 
| 
      
 9 
     | 
    
         
            +
                should "correctly report its given dimensions" do
         
     | 
| 
      
 10 
     | 
    
         
            +
                  assert @geo = Paperclip::Geometry.new(1024, 768)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  assert_equal 1024, @geo.width
         
     | 
| 
      
 12 
     | 
    
         
            +
                  assert_equal 768, @geo.height
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                should "correctly create a square if the height dimension is missing" do
         
     | 
| 
      
 16 
     | 
    
         
            +
                  assert @geo = Paperclip::Geometry.new(1024)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  assert_equal 1024, @geo.width
         
     | 
| 
      
 18 
     | 
    
         
            +
                  assert_equal 1024, @geo.height
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                should "correctly create a square if the width dimension is missing" do
         
     | 
| 
      
 22 
     | 
    
         
            +
                  assert @geo = Paperclip::Geometry.new(nil, 768)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  assert_equal 768, @geo.width
         
     | 
| 
      
 24 
     | 
    
         
            +
                  assert_equal 768, @geo.height
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                should "be generated from a WxH-formatted string" do
         
     | 
| 
      
 28 
     | 
    
         
            +
                  assert @geo = Paperclip::Geometry.parse("800x600")
         
     | 
| 
      
 29 
     | 
    
         
            +
                  assert_equal 800, @geo.width
         
     | 
| 
      
 30 
     | 
    
         
            +
                  assert_equal 600, @geo.height
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                should "be generated from a xH-formatted string" do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  assert @geo = Paperclip::Geometry.parse("x600")
         
     | 
| 
      
 35 
     | 
    
         
            +
                  assert_equal 600, @geo.width
         
     | 
| 
      
 36 
     | 
    
         
            +
                  assert_equal 600, @geo.height
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  
         
     | 
| 
      
 39 
     | 
    
         
            +
                should "be generated from a Wx-formatted string" do
         
     | 
| 
      
 40 
     | 
    
         
            +
                  assert @geo = Paperclip::Geometry.parse("800x")
         
     | 
| 
      
 41 
     | 
    
         
            +
                  assert_equal 800, @geo.width
         
     | 
| 
      
 42 
     | 
    
         
            +
                  assert_equal 800, @geo.height
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                should "ensure the modifier is nil if only one dimension present" do
         
     | 
| 
      
 46 
     | 
    
         
            +
                  assert @geo = Paperclip::Geometry.parse("123x")
         
     | 
| 
      
 47 
     | 
    
         
            +
                  assert_nil @geo.modifier
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                should "ensure the modifier is nil if not present" do
         
     | 
| 
      
 51 
     | 
    
         
            +
                  assert @geo = Paperclip::Geometry.parse("123x456")
         
     | 
| 
      
 52 
     | 
    
         
            +
                  assert_nil @geo.modifier
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                ['>', '<', '#', '@', '%', '^', '!'].each do |mod|
         
     | 
| 
      
 56 
     | 
    
         
            +
                  should "ensure the modifier #{mod} is preserved" do
         
     | 
| 
      
 57 
     | 
    
         
            +
                    assert @geo = Paperclip::Geometry.parse("123x456#{mod}")
         
     | 
| 
      
 58 
     | 
    
         
            +
                    assert_equal mod, @geo.modifier
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                should "make sure the modifier gets passed during transformation_to" do
         
     | 
| 
      
 63 
     | 
    
         
            +
                  assert @src = Paperclip::Geometry.parse("123x456")
         
     | 
| 
      
 64 
     | 
    
         
            +
                  assert @dst = Paperclip::Geometry.parse("123x456>")
         
     | 
| 
      
 65 
     | 
    
         
            +
                  assert_equal "123x456>", @src.transformation_to(@dst).to_s
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                should "be generated from a file" do
         
     | 
| 
      
 69 
     | 
    
         
            +
                  file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
         
     | 
| 
      
 70 
     | 
    
         
            +
                  file = File.new(file)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
         
     | 
| 
      
 72 
     | 
    
         
            +
                  assert @geo.height > 0
         
     | 
| 
      
 73 
     | 
    
         
            +
                  assert @geo.width > 0
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                should "be generated from a file path" do
         
     | 
| 
      
 77 
     | 
    
         
            +
                  file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
         
     | 
| 
      
 78 
     | 
    
         
            +
                  assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
         
     | 
| 
      
 79 
     | 
    
         
            +
                  assert @geo.height > 0
         
     | 
| 
      
 80 
     | 
    
         
            +
                  assert @geo.width > 0
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                should "not generate from a bad file" do
         
     | 
| 
      
 84 
     | 
    
         
            +
                  file = "/home/This File Does Not Exist.omg"
         
     | 
| 
      
 85 
     | 
    
         
            +
                  assert_raise(Errno::ENOENT){ @geo = Paperclip::Geometry.from_file(file) }
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                [['vertical',   900,  1440, true,  false, false, 1440, 900, 0.625],
         
     | 
| 
      
 89 
     | 
    
         
            +
                 ['horizontal', 1024, 768,  false, true,  false, 1024, 768, 1.3333],
         
     | 
| 
      
 90 
     | 
    
         
            +
                 ['square',     100,  100,  false, false, true,  100,  100, 1]].each do |args|
         
     | 
| 
      
 91 
     | 
    
         
            +
                  context "performing calculations on a #{args[0]} viewport" do
         
     | 
| 
      
 92 
     | 
    
         
            +
                    setup do
         
     | 
| 
      
 93 
     | 
    
         
            +
                      @geo = Paperclip::Geometry.new(args[1], args[2])
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                    should "#{args[3] ? "" : "not"} be vertical" do
         
     | 
| 
      
 97 
     | 
    
         
            +
                      assert_equal args[3], @geo.vertical?
         
     | 
| 
      
 98 
     | 
    
         
            +
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                    should "#{args[4] ? "" : "not"} be horizontal" do
         
     | 
| 
      
 101 
     | 
    
         
            +
                      assert_equal args[4], @geo.horizontal?
         
     | 
| 
      
 102 
     | 
    
         
            +
                    end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    should "#{args[5] ? "" : "not"} be square" do
         
     | 
| 
      
 105 
     | 
    
         
            +
                      assert_equal args[5], @geo.square?
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                    should "report that #{args[6]} is the larger dimension" do
         
     | 
| 
      
 109 
     | 
    
         
            +
                      assert_equal args[6], @geo.larger
         
     | 
| 
      
 110 
     | 
    
         
            +
                    end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                    should "report that #{args[7]} is the smaller dimension" do
         
     | 
| 
      
 113 
     | 
    
         
            +
                      assert_equal args[7], @geo.smaller
         
     | 
| 
      
 114 
     | 
    
         
            +
                    end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                    should "have an aspect ratio of #{args[8]}" do
         
     | 
| 
      
 117 
     | 
    
         
            +
                      assert_in_delta args[8], @geo.aspect, 0.0001
         
     | 
| 
      
 118 
     | 
    
         
            +
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
                  end
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                [[ [1000, 100], [64, 64],  "x64", "64x64+288+0" ],
         
     | 
| 
      
 123 
     | 
    
         
            +
                 [ [100, 1000], [50, 950], "x950", "50x950+22+0" ],
         
     | 
| 
      
 124 
     | 
    
         
            +
                 [ [100, 1000], [50, 25],  "50x", "50x25+0+237" ]]. each do |args|
         
     | 
| 
      
 125 
     | 
    
         
            +
                  context "of #{args[0].inspect} and given a Geometry #{args[1].inspect} and sent transform_to" do
         
     | 
| 
      
 126 
     | 
    
         
            +
                    setup do
         
     | 
| 
      
 127 
     | 
    
         
            +
                      @geo = Paperclip::Geometry.new(*args[0])
         
     | 
| 
      
 128 
     | 
    
         
            +
                      @dst = Paperclip::Geometry.new(*args[1])
         
     | 
| 
      
 129 
     | 
    
         
            +
                      @scale, @crop = @geo.transformation_to @dst, true
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                    should "be able to return the correct scaling transformation geometry #{args[2]}" do
         
     | 
| 
      
 133 
     | 
    
         
            +
                      assert_equal args[2], @scale
         
     | 
| 
      
 134 
     | 
    
         
            +
                    end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                    should "be able to return the correct crop transformation geometry #{args[3]}" do
         
     | 
| 
      
 137 
     | 
    
         
            +
                      assert_equal args[3], @crop
         
     | 
| 
      
 138 
     | 
    
         
            +
                    end
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
              end
         
     | 
| 
      
 142 
     | 
    
         
            +
            end
         
     |