diff options
| author | hukl <contact@smyck.org> | 2009-04-24 11:43:08 +0200 |
|---|---|---|
| committer | hukl <contact@smyck.org> | 2009-04-25 14:55:27 +0200 |
| commit | cf1b60e0cfa7d1a8f4a80d686649cc12e73a634e (patch) | |
| tree | b68bd845d290ce968892c4532bcff52083925834 /vendor | |
| parent | b2b78c06074046bd73cc3408a29386a976f0469c (diff) | |
Integrated basic Asset upload functionality. You can upload files now and use their url in pages.
Diffstat (limited to 'vendor')
44 files changed, 4458 insertions, 0 deletions
diff --git a/vendor/plugins/paperclip/LICENSE b/vendor/plugins/paperclip/LICENSE new file mode 100644 index 0000000..299b9ed --- /dev/null +++ b/vendor/plugins/paperclip/LICENSE | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | |||
| 2 | LICENSE | ||
| 3 | |||
| 4 | The MIT License | ||
| 5 | |||
| 6 | Copyright (c) 2008 Jon Yurek and thoughtbot, inc. | ||
| 7 | |||
| 8 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 9 | of this software and associated documentation files (the "Software"), to deal | ||
| 10 | in the Software without restriction, including without limitation the rights | ||
| 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 12 | copies of the Software, and to permit persons to whom the Software is | ||
| 13 | furnished to do so, subject to the following conditions: | ||
| 14 | |||
| 15 | The above copyright notice and this permission notice shall be included in | ||
| 16 | all copies or substantial portions of the Software. | ||
| 17 | |||
| 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 24 | THE SOFTWARE. | ||
| 25 | |||
| 26 | |||
diff --git a/vendor/plugins/paperclip/README.rdoc b/vendor/plugins/paperclip/README.rdoc new file mode 100644 index 0000000..85fcf4b --- /dev/null +++ b/vendor/plugins/paperclip/README.rdoc | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | =Paperclip | ||
| 2 | |||
| 3 | Paperclip is intended as an easy file attachment library for ActiveRecord. The | ||
| 4 | intent behind it was to keep setup as easy as possible and to treat files as | ||
| 5 | much like other attributes as possible. This means they aren't saved to their | ||
| 6 | final locations on disk, nor are they deleted if set to nil, until | ||
| 7 | ActiveRecord::Base#save is called. It manages validations based on size and | ||
| 8 | presence, if required. It can transform its assigned image into thumbnails if | ||
| 9 | needed, and the prerequisites are as simple as installing ImageMagick (which, | ||
| 10 | for most modern Unix-based systems, is as easy as installing the right | ||
| 11 | packages). Attached files are saved to the filesystem and referenced in the | ||
| 12 | browser by an easily understandable specification, which has sensible and | ||
| 13 | useful defaults. | ||
| 14 | |||
| 15 | See the documentation for +has_attached_file+ in Paperclip::ClassMethods for | ||
| 16 | more detailed options. | ||
| 17 | |||
| 18 | ==Quick Start | ||
| 19 | |||
| 20 | In your model: | ||
| 21 | |||
| 22 | class User < ActiveRecord::Base | ||
| 23 | has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" } | ||
| 24 | end | ||
| 25 | |||
| 26 | In your migrations: | ||
| 27 | |||
| 28 | class AddAvatarColumnsToUser < ActiveRecord::Migration | ||
| 29 | def self.up | ||
| 30 | add_column :users, :avatar_file_name, :string | ||
| 31 | add_column :users, :avatar_content_type, :string | ||
| 32 | add_column :users, :avatar_file_size, :integer | ||
| 33 | add_column :users, :avatar_updated_at, :datetime | ||
| 34 | end | ||
| 35 | |||
| 36 | def self.down | ||
| 37 | remove_column :users, :avatar_file_name | ||
| 38 | remove_column :users, :avatar_content_type | ||
| 39 | remove_column :users, :avatar_file_size | ||
| 40 | remove_column :users, :avatar_updated_at | ||
| 41 | end | ||
| 42 | end | ||
| 43 | |||
| 44 | In your edit and new views: | ||
| 45 | |||
| 46 | <% form_for :user, @user, :url => user_path, :html => { :multipart => true } do |form| %> | ||
| 47 | <%= form.file_field :avatar %> | ||
| 48 | <% end %> | ||
| 49 | |||
| 50 | In your controller: | ||
| 51 | |||
| 52 | def create | ||
| 53 | @user = User.create( params[:user] ) | ||
| 54 | end | ||
| 55 | |||
| 56 | In your show view: | ||
| 57 | |||
| 58 | <%= image_tag @user.avatar.url %> | ||
| 59 | <%= image_tag @user.avatar.url(:medium) %> | ||
| 60 | <%= image_tag @user.avatar.url(:thumb) %> | ||
| 61 | |||
| 62 | ==Usage | ||
| 63 | |||
| 64 | The basics of paperclip are quite simple: Declare that your model has an | ||
| 65 | attachment with the has_attached_file method, and give it a name. Paperclip | ||
| 66 | will wrap up up to four attributes (all prefixed with that attachment's name, | ||
| 67 | so you can have multiple attachments per model if you wish) and give the a | ||
| 68 | friendly front end. The attributes are <attachment>_file_name, | ||
| 69 | <attachment>_file_size, <attachment>_content_type, and <attachment>_updated_at. | ||
| 70 | Only <attachment>_file_name is required for paperclip to operate. More | ||
| 71 | information about the options to has_attached_file is available in the | ||
| 72 | documentation of Paperclip::ClassMethods. | ||
| 73 | |||
| 74 | Attachments can be validated with Paperclip's validation methods, | ||
| 75 | validates_attachment_presence, validates_attachment_content_type, and | ||
| 76 | validates_attachment_size. | ||
| 77 | |||
| 78 | ==Storage | ||
| 79 | |||
| 80 | The files that are assigned as attachments are, by default, placed in the | ||
| 81 | directory specified by the :path option to has_attached_file. By default, this | ||
| 82 | location is | ||
| 83 | ":rails_root/public/system/:attachment/:id/:style/:basename.:extension". This | ||
| 84 | location was chosen because on standard Capistrano deployments, the | ||
| 85 | public/system directory is symlinked to the app's shared directory, meaning it | ||
| 86 | will survive between deployments. For example, using that :path, you may have a | ||
| 87 | file at | ||
| 88 | |||
| 89 | /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png | ||
| 90 | |||
| 91 | NOTE: This is a change from previous versions of Paperclip, but is overall a | ||
| 92 | safer choice for the defaul file store. | ||
| 93 | |||
| 94 | You may also choose to store your files using Amazon's S3 service. You can find | ||
| 95 | more information about S3 storage at the description for | ||
| 96 | Paperclip::Storage::S3. | ||
| 97 | |||
| 98 | Files on the local filesystem (and in the Rails app's public directory) will be | ||
| 99 | available to the internet at large. If you require access control, it's | ||
| 100 | possible to place your files in a different location. You will need to change | ||
| 101 | both the :path and :url options in order to make sure the files are unavailable | ||
| 102 | to the public. Both :path and :url allow the same set of interpolated | ||
| 103 | variables. | ||
| 104 | |||
| 105 | ==Post Processing | ||
| 106 | |||
| 107 | Paperclip supports an extendible selection of post-processors. When you define | ||
| 108 | a set of styles for an attachment, by default it is expected that those | ||
| 109 | "styles" are actually "thumbnails". However, you can do more than just | ||
| 110 | thumbnail images. By defining a subclass of Paperclip::Processor, you can | ||
| 111 | perform any processing you want on the files that are attached. Any file in | ||
| 112 | your Rails app's lib/paperclip_processors directory is automatically loaded by | ||
| 113 | paperclip, allowing you to easily define custom processors. You can specify a | ||
| 114 | processor with the :processors option to has_attached_file: | ||
| 115 | |||
| 116 | has_attached_file :scan, :styles => { :text => { :quality => :better } }, | ||
| 117 | :processors => [:ocr] | ||
| 118 | |||
| 119 | This would load the hypothetical class Paperclip::Ocr, which would have the | ||
| 120 | hash "{ :quality => :better }" passed to it along with the uploaded file. For | ||
| 121 | more information about defining processors, see Paperclip::Processor. | ||
| 122 | |||
| 123 | The default processor is Paperclip::Thumbnail. For backwards compatability | ||
| 124 | reasons, you can pass a single geometry string or an array containing a | ||
| 125 | geometry and a format, which the file will be converted to, like so: | ||
| 126 | |||
| 127 | has_attached_file :avatar, :styles => { :thumb => ["32x32#", :png] } | ||
| 128 | |||
| 129 | This will convert the "thumb" style to a 32x32 square in png format, regardless | ||
| 130 | of what was uploaded. If the format is not specified, it is kept the same (i.e. | ||
| 131 | jpgs will remain jpgs). | ||
| 132 | |||
| 133 | Multiple processors can be specified, and they will be invoked in the order | ||
| 134 | they are defined in the :processors array. Each successive processor will | ||
| 135 | be given the result of the previous processor's execution. All processors will | ||
| 136 | receive the same parameters, which are what you define in the :styles hash. | ||
| 137 | For example, assuming we had this definition: | ||
| 138 | |||
| 139 | has_attached_file :scan, :styles => { :text => { :quality => :better } }, | ||
| 140 | :processors => [:rotator, :ocr] | ||
| 141 | |||
| 142 | then both the :rotator processor and the :ocr processor would receive the | ||
| 143 | options "{ :quality => :better }". This parameter may not mean anything to one | ||
| 144 | or more or the processors, and they are free to ignore it. | ||
| 145 | |||
| 146 | ==Events | ||
| 147 | |||
| 148 | Before and after the Post Processing step, Paperclip calls back to the model | ||
| 149 | with a few callbacks, allowing the model to change or cancel the processing | ||
| 150 | step. The callbacks are "before_post_process" and "after_post_process" (which | ||
| 151 | are called before and after the processing of each attachment), and the | ||
| 152 | attachment-specific "before_<attachment>_post_process" and | ||
| 153 | "after_<attachment>_post_process". The callbacks are intended to be as close to | ||
| 154 | normal ActiveRecord callbacks as possible, so if you return false (specifically | ||
| 155 | - returning nil is not the same) in a before_ filter, the post processing step | ||
| 156 | will halt. Returning false in an after_ filter will not halt anything, but you | ||
| 157 | can access the model and the attachment if necessary. | ||
| 158 | |||
| 159 | NOTE: Post processing will not even *start* if the attachment is not valid | ||
| 160 | according to the validations. Your callbacks (and processors) will only be | ||
| 161 | called with valid attachments. | ||
| 162 | |||
| 163 | ==Contributing | ||
| 164 | |||
| 165 | If you'd like to contribute a feature or bugfix: Thanks! To make sure your | ||
| 166 | fix/feature has a high chance of being included, please read the following | ||
| 167 | guidelines: | ||
| 168 | |||
| 169 | 1. Ask on the mailing list, or post a ticket in Lighthouse. | ||
| 170 | 2. Make sure there are tests! We will not accept any patch that is not tested. | ||
| 171 | It's a rare time when explicit tests aren't needed. If you have questions | ||
| 172 | about writing tests for paperclip, please ask the mailing list. | ||
diff --git a/vendor/plugins/paperclip/Rakefile b/vendor/plugins/paperclip/Rakefile new file mode 100644 index 0000000..91f1687 --- /dev/null +++ b/vendor/plugins/paperclip/Rakefile | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | require 'rake' | ||
| 2 | require 'rake/testtask' | ||
| 3 | require 'rake/rdoctask' | ||
| 4 | |||
| 5 | $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib') | ||
| 6 | require 'paperclip' | ||
| 7 | |||
| 8 | desc 'Default: run unit tests.' | ||
| 9 | task :default => [:clean, :test] | ||
| 10 | |||
| 11 | desc 'Test the paperclip plugin.' | ||
| 12 | Rake::TestTask.new(:test) do |t| | ||
| 13 | t.libs << 'lib' << 'profile' | ||
| 14 | t.pattern = 'test/**/*_test.rb' | ||
| 15 | t.verbose = true | ||
| 16 | end | ||
| 17 | |||
| 18 | desc 'Start an IRB session with all necessary files required.' | ||
| 19 | task :shell do |t| | ||
| 20 | chdir File.dirname(__FILE__) | ||
| 21 | exec 'irb -I lib/ -I lib/paperclip -r rubygems -r active_record -r tempfile -r init' | ||
| 22 | end | ||
| 23 | |||
| 24 | desc 'Generate documentation for the paperclip plugin.' | ||
| 25 | Rake::RDocTask.new(:rdoc) do |rdoc| | ||
| 26 | rdoc.rdoc_dir = 'doc' | ||
| 27 | rdoc.title = 'Paperclip' | ||
| 28 | rdoc.options << '--line-numbers' << '--inline-source' | ||
| 29 | rdoc.rdoc_files.include('README*') | ||
| 30 | rdoc.rdoc_files.include('lib/**/*.rb') | ||
| 31 | end | ||
| 32 | |||
| 33 | desc 'Update documentation on website' | ||
| 34 | task :sync_docs => 'rdoc' do | ||
| 35 | `rsync -ave ssh doc/ dev@dev.thoughtbot.com:/home/dev/www/dev.thoughtbot.com/paperclip` | ||
| 36 | end | ||
| 37 | |||
| 38 | desc 'Clean up files.' | ||
| 39 | task :clean do |t| | ||
| 40 | FileUtils.rm_rf "doc" | ||
| 41 | FileUtils.rm_rf "tmp" | ||
| 42 | FileUtils.rm_rf "pkg" | ||
| 43 | FileUtils.rm "test/debug.log" rescue nil | ||
| 44 | FileUtils.rm "test/paperclip.db" rescue nil | ||
| 45 | end | ||
| 46 | |||
| 47 | spec = Gem::Specification.new do |s| | ||
| 48 | s.name = "paperclip" | ||
| 49 | s.version = Paperclip::VERSION | ||
| 50 | s.author = "Jon Yurek" | ||
| 51 | s.email = "jyurek@thoughtbot.com" | ||
| 52 | s.homepage = "http://www.thoughtbot.com/projects/paperclip" | ||
| 53 | s.platform = Gem::Platform::RUBY | ||
| 54 | s.summary = "File attachments as attributes for ActiveRecord" | ||
| 55 | s.files = FileList["README*", | ||
| 56 | "LICENSE", | ||
| 57 | "Rakefile", | ||
| 58 | "init.rb", | ||
| 59 | "{generators,lib,tasks,test,shoulda_macros}/**/*"].to_a | ||
| 60 | s.require_path = "lib" | ||
| 61 | s.test_files = FileList["test/**/test_*.rb"].to_a | ||
| 62 | s.rubyforge_project = "paperclip" | ||
| 63 | s.has_rdoc = true | ||
| 64 | s.extra_rdoc_files = FileList["README*"].to_a | ||
| 65 | s.rdoc_options << '--line-numbers' << '--inline-source' | ||
| 66 | s.requirements << "ImageMagick" | ||
| 67 | s.add_runtime_dependency 'right_aws' | ||
| 68 | s.add_development_dependency 'thoughtbot-shoulda' | ||
| 69 | s.add_development_dependency 'mocha' | ||
| 70 | end | ||
| 71 | |||
| 72 | desc "Generate a gemspec file for GitHub" | ||
| 73 | task :gemspec do | ||
| 74 | File.open("#{spec.name}.gemspec", 'w') do |f| | ||
| 75 | f.write spec.to_ruby | ||
| 76 | end | ||
| 77 | end | ||
diff --git a/vendor/plugins/paperclip/generators/paperclip/USAGE b/vendor/plugins/paperclip/generators/paperclip/USAGE new file mode 100644 index 0000000..2d611d7 --- /dev/null +++ b/vendor/plugins/paperclip/generators/paperclip/USAGE | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | Usage: | ||
| 2 | |||
| 3 | script/generate paperclip Class attachment1 (attachment2 ...) | ||
| 4 | |||
| 5 | This will create a migration that will add the proper columns to your class's table. \ No newline at end of file | ||
diff --git a/vendor/plugins/paperclip/generators/paperclip/paperclip_generator.rb b/vendor/plugins/paperclip/generators/paperclip/paperclip_generator.rb new file mode 100644 index 0000000..77ee5a2 --- /dev/null +++ b/vendor/plugins/paperclip/generators/paperclip/paperclip_generator.rb | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | class PaperclipGenerator < Rails::Generator::NamedBase | ||
| 2 | attr_accessor :attachments, :migration_name | ||
| 3 | |||
| 4 | def initialize(args, options = {}) | ||
| 5 | super | ||
| 6 | @class_name, @attachments = args[0], args[1..-1] | ||
| 7 | end | ||
| 8 | |||
| 9 | def manifest | ||
| 10 | file_name = generate_file_name | ||
| 11 | @migration_name = file_name.camelize | ||
| 12 | record do |m| | ||
| 13 | m.migration_template "paperclip_migration.rb.erb", | ||
| 14 | File.join('db', 'migrate'), | ||
| 15 | :migration_file_name => file_name | ||
| 16 | end | ||
| 17 | end | ||
| 18 | |||
| 19 | private | ||
| 20 | |||
| 21 | def generate_file_name | ||
| 22 | names = attachments.map{|a| a.underscore } | ||
| 23 | names = names[0..-2] + ["and", names[-1]] if names.length > 1 | ||
| 24 | "add_attachments_#{names.join("_")}_to_#{@class_name.underscore}" | ||
| 25 | end | ||
| 26 | |||
| 27 | end | ||
diff --git a/vendor/plugins/paperclip/generators/paperclip/templates/paperclip_migration.rb.erb b/vendor/plugins/paperclip/generators/paperclip/templates/paperclip_migration.rb.erb new file mode 100644 index 0000000..eebb0e5 --- /dev/null +++ b/vendor/plugins/paperclip/generators/paperclip/templates/paperclip_migration.rb.erb | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | class <%= migration_name %> < ActiveRecord::Migration | ||
| 2 | def self.up | ||
| 3 | <% attachments.each do |attachment| -%> | ||
| 4 | add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string | ||
| 5 | add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string | ||
| 6 | add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_size, :integer | ||
| 7 | add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at, :datetime | ||
| 8 | <% end -%> | ||
| 9 | end | ||
| 10 | |||
| 11 | def self.down | ||
| 12 | <% attachments.each do |attachment| -%> | ||
| 13 | remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name | ||
| 14 | remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type | ||
| 15 | remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_size | ||
| 16 | remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at | ||
| 17 | <% end -%> | ||
| 18 | end | ||
| 19 | end | ||
diff --git a/vendor/plugins/paperclip/init.rb b/vendor/plugins/paperclip/init.rb new file mode 100644 index 0000000..5a07dda --- /dev/null +++ b/vendor/plugins/paperclip/init.rb | |||
| @@ -0,0 +1 @@ | |||
| require File.join(File.dirname(__FILE__), "lib", "paperclip") | |||
diff --git a/vendor/plugins/paperclip/lib/paperclip.rb b/vendor/plugins/paperclip/lib/paperclip.rb new file mode 100644 index 0000000..74c3d79 --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip.rb | |||
| @@ -0,0 +1,318 @@ | |||
| 1 | # Paperclip allows file attachments that are stored in the filesystem. All graphical | ||
| 2 | # transformations are done using the Graphics/ImageMagick command line utilities and | ||
| 3 | # are stored in Tempfiles until the record is saved. Paperclip does not require a | ||
| 4 | # separate model for storing the attachment's information, instead adding a few simple | ||
| 5 | # columns to your table. | ||
| 6 | # | ||
| 7 | # Author:: Jon Yurek | ||
| 8 | # Copyright:: Copyright (c) 2008 thoughtbot, inc. | ||
| 9 | # License:: MIT License (http://www.opensource.org/licenses/mit-license.php) | ||
| 10 | # | ||
| 11 | # Paperclip defines an attachment as any file, though it makes special considerations | ||
| 12 | # for image files. You can declare that a model has an attached file with the | ||
| 13 | # +has_attached_file+ method: | ||
| 14 | # | ||
| 15 | # class User < ActiveRecord::Base | ||
| 16 | # has_attached_file :avatar, :styles => { :thumb => "100x100" } | ||
| 17 | # end | ||
| 18 | # | ||
| 19 | # user = User.new | ||
| 20 | # user.avatar = params[:user][:avatar] | ||
| 21 | # user.avatar.url | ||
| 22 | # # => "/users/avatars/4/original_me.jpg" | ||
| 23 | # user.avatar.url(:thumb) | ||
| 24 | # # => "/users/avatars/4/thumb_me.jpg" | ||
| 25 | # | ||
| 26 | # See the +has_attached_file+ documentation for more details. | ||
| 27 | |||
| 28 | require 'tempfile' | ||
| 29 | require 'paperclip/upfile' | ||
| 30 | require 'paperclip/iostream' | ||
| 31 | require 'paperclip/geometry' | ||
| 32 | require 'paperclip/processor' | ||
| 33 | require 'paperclip/thumbnail' | ||
| 34 | require 'paperclip/storage' | ||
| 35 | require 'paperclip/attachment' | ||
| 36 | if defined? RAILS_ROOT | ||
| 37 | Dir.glob(File.join(File.expand_path(RAILS_ROOT), "lib", "paperclip_processors", "*.rb")).each do |processor| | ||
| 38 | require processor | ||
| 39 | end | ||
| 40 | end | ||
| 41 | |||
| 42 | # The base module that gets included in ActiveRecord::Base. See the | ||
| 43 | # documentation for Paperclip::ClassMethods for more useful information. | ||
| 44 | module Paperclip | ||
| 45 | |||
| 46 | VERSION = "2.2.8" | ||
| 47 | |||
| 48 | class << self | ||
| 49 | # Provides configurability to Paperclip. There are a number of options available, such as: | ||
| 50 | # * whiny_thumbnails: Will raise an error if Paperclip cannot process thumbnails of | ||
| 51 | # an uploaded image. Defaults to true. | ||
| 52 | # * log: Logs progress to the Rails log. Uses ActiveRecord's logger, so honors | ||
| 53 | # log levels, etc. Defaults to true. | ||
| 54 | # * command_path: Defines the path at which to find the command line | ||
| 55 | # programs if they are not visible to Rails the system's search path. Defaults to | ||
| 56 | # nil, which uses the first executable found in the user's search path. | ||
| 57 | # * image_magick_path: Deprecated alias of command_path. | ||
| 58 | def options | ||
| 59 | @options ||= { | ||
| 60 | :whiny_thumbnails => true, | ||
| 61 | :image_magick_path => nil, | ||
| 62 | :command_path => nil, | ||
| 63 | :log => true, | ||
| 64 | :swallow_stderr => true | ||
| 65 | } | ||
| 66 | end | ||
| 67 | |||
| 68 | def path_for_command command #:nodoc: | ||
| 69 | if options[:image_magick_path] | ||
| 70 | warn("[DEPRECATION] :image_magick_path is deprecated and will be removed. Use :command_path instead") | ||
| 71 | end | ||
| 72 | path = [options[:command_path] || options[:image_magick_path], command].compact | ||
| 73 | File.join(*path) | ||
| 74 | end | ||
| 75 | |||
| 76 | def interpolates key, &block | ||
| 77 | Paperclip::Attachment.interpolations[key] = block | ||
| 78 | end | ||
| 79 | |||
| 80 | # The run method takes a command to execute and a string of parameters | ||
| 81 | # that get passed to it. The command is prefixed with the :command_path | ||
| 82 | # option from Paperclip.options. If you have many commands to run and | ||
| 83 | # they are in different paths, the suggested course of action is to | ||
| 84 | # symlink them so they are all in the same directory. | ||
| 85 | # | ||
| 86 | # If the command returns with a result code that is not one of the | ||
| 87 | # expected_outcodes, a PaperclipCommandLineError will be raised. Generally | ||
| 88 | # a code of 0 is expected, but a list of codes may be passed if necessary. | ||
| 89 | def run cmd, params = "", expected_outcodes = 0 | ||
| 90 | command = %Q<#{%Q[#{path_for_command(cmd)} #{params}].gsub(/\s+/, " ")}> | ||
| 91 | command = "#{command} 2>#{bit_bucket}" if Paperclip.options[:swallow_stderr] | ||
| 92 | output = `#{command}` | ||
| 93 | unless [expected_outcodes].flatten.include?($?.exitstatus) | ||
| 94 | raise PaperclipCommandLineError, "Error while running #{cmd}" | ||
| 95 | end | ||
| 96 | output | ||
| 97 | end | ||
| 98 | |||
| 99 | def bit_bucket #:nodoc: | ||
| 100 | File.exists?("/dev/null") ? "/dev/null" : "NUL" | ||
| 101 | end | ||
| 102 | |||
| 103 | def included base #:nodoc: | ||
| 104 | base.extend ClassMethods | ||
| 105 | unless base.respond_to?(:define_callbacks) | ||
| 106 | base.send(:include, Paperclip::CallbackCompatability) | ||
| 107 | end | ||
| 108 | end | ||
| 109 | |||
| 110 | def processor name #:nodoc: | ||
| 111 | name = name.to_s.camelize | ||
| 112 | processor = Paperclip.const_get(name) | ||
| 113 | unless processor.ancestors.include?(Paperclip::Processor) | ||
| 114 | raise PaperclipError.new("Processor #{name} was not found") | ||
| 115 | end | ||
| 116 | processor | ||
| 117 | end | ||
| 118 | end | ||
| 119 | |||
| 120 | class PaperclipError < StandardError #:nodoc: | ||
| 121 | end | ||
| 122 | |||
| 123 | class PaperclipCommandLineError < StandardError #:nodoc: | ||
| 124 | end | ||
| 125 | |||
| 126 | class NotIdentifiedByImageMagickError < PaperclipError #:nodoc: | ||
| 127 | end | ||
| 128 | |||
| 129 | module ClassMethods | ||
| 130 | # +has_attached_file+ gives the class it is called on an attribute that maps to a file. This | ||
| 131 | # is typically a file stored somewhere on the filesystem and has been uploaded by a user. | ||
| 132 | # The attribute returns a Paperclip::Attachment object which handles the management of | ||
| 133 | # that file. The intent is to make the attachment as much like a normal attribute. The | ||
| 134 | # thumbnails will be created when the new file is assigned, but they will *not* be saved | ||
| 135 | # until +save+ is called on the record. Likewise, if the attribute is set to +nil+ is | ||
| 136 | # called on it, the attachment will *not* be deleted until +save+ is called. See the | ||
| 137 | # Paperclip::Attachment documentation for more specifics. There are a number of options | ||
| 138 | # you can set to change the behavior of a Paperclip attachment: | ||
| 139 | # * +url+: The full URL of where the attachment is publically accessible. This can just | ||
| 140 | # as easily point to a directory served directly through Apache as it can to an action | ||
| 141 | # that can control permissions. You can specify the full domain and path, but usually | ||
| 142 | # just an absolute path is sufficient. The leading slash *must* be included manually for | ||
| 143 | # absolute paths. The default value is | ||
| 144 | # "/system/:attachment/:id/:style/:basename.:extension". See | ||
| 145 | # Paperclip::Attachment#interpolate for more information on variable interpolaton. | ||
| 146 | # :url => "/:class/:attachment/:id/:style_:basename.:extension" | ||
| 147 | # :url => "http://some.other.host/stuff/:class/:id_:extension" | ||
| 148 | # * +default_url+: The URL that will be returned if there is no attachment assigned. | ||
| 149 | # This field is interpolated just as the url is. The default value is | ||
| 150 | # "/:attachment/:style/missing.png" | ||
| 151 | # has_attached_file :avatar, :default_url => "/images/default_:style_avatar.png" | ||
| 152 | # User.new.avatar_url(:small) # => "/images/default_small_avatar.png" | ||
| 153 | # * +styles+: A hash of thumbnail styles and their geometries. You can find more about | ||
| 154 | # geometry strings at the ImageMagick website | ||
| 155 | # (http://www.imagemagick.org/script/command-line-options.php#resize). Paperclip | ||
| 156 | # also adds the "#" option (e.g. "50x50#"), which will resize the image to fit maximally | ||
| 157 | # inside the dimensions and then crop the rest off (weighted at the center). The | ||
| 158 | # default value is to generate no thumbnails. | ||
| 159 | # * +default_style+: The thumbnail style that will be used by default URLs. | ||
| 160 | # Defaults to +original+. | ||
| 161 | # has_attached_file :avatar, :styles => { :normal => "100x100#" }, | ||
| 162 | # :default_style => :normal | ||
| 163 | # user.avatar.url # => "/avatars/23/normal_me.png" | ||
| 164 | # * +whiny_thumbnails+: Will raise an error if Paperclip cannot post_process an uploaded file due | ||
| 165 | # to a command line error. This will override the global setting for this attachment. | ||
| 166 | # Defaults to true. | ||
| 167 | # * +convert_options+: When creating thumbnails, use this free-form options | ||
| 168 | # field to pass in various convert command options. Typical options are "-strip" to | ||
| 169 | # remove all Exif data from the image (save space for thumbnails and avatars) or | ||
| 170 | # "-depth 8" to specify the bit depth of the resulting conversion. See ImageMagick | ||
| 171 | # convert documentation for more options: (http://www.imagemagick.org/script/convert.php) | ||
| 172 | # Note that this option takes a hash of options, each of which correspond to the style | ||
| 173 | # of thumbnail being generated. You can also specify :all as a key, which will apply | ||
| 174 | # to all of the thumbnails being generated. If you specify options for the :original, | ||
| 175 | # it would be best if you did not specify destructive options, as the intent of keeping | ||
| 176 | # the original around is to regenerate all the thumbnails when requirements change. | ||
| 177 | # has_attached_file :avatar, :styles => { :large => "300x300", :negative => "100x100" } | ||
| 178 | # :convert_options => { | ||
| 179 | # :all => "-strip", | ||
| 180 | # :negative => "-negate" | ||
| 181 | # } | ||
| 182 | # * +storage+: Chooses the storage backend where the files will be stored. The current | ||
| 183 | # choices are :filesystem and :s3. The default is :filesystem. Make sure you read the | ||
| 184 | # documentation for Paperclip::Storage::Filesystem and Paperclip::Storage::S3 | ||
| 185 | # for backend-specific options. | ||
| 186 | def has_attached_file name, options = {} | ||
| 187 | include InstanceMethods | ||
| 188 | |||
| 189 | write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil? | ||
| 190 | attachment_definitions[name] = {:validations => {}}.merge(options) | ||
| 191 | |||
| 192 | after_save :save_attached_files | ||
| 193 | before_destroy :destroy_attached_files | ||
| 194 | |||
| 195 | define_callbacks :before_post_process, :after_post_process | ||
| 196 | define_callbacks :"before_#{name}_post_process", :"after_#{name}_post_process" | ||
| 197 | |||
| 198 | define_method name do |*args| | ||
| 199 | a = attachment_for(name) | ||
| 200 | (args.length > 0) ? a.to_s(args.first) : a | ||
| 201 | end | ||
| 202 | |||
| 203 | define_method "#{name}=" do |file| | ||
| 204 | attachment_for(name).assign(file) | ||
| 205 | end | ||
| 206 | |||
| 207 | define_method "#{name}?" do | ||
| 208 | attachment_for(name).file? | ||
| 209 | end | ||
| 210 | |||
| 211 | validates_each(name) do |record, attr, value| | ||
| 212 | attachment = record.attachment_for(name) | ||
| 213 | attachment.send(:flush_errors) unless attachment.valid? | ||
| 214 | end | ||
| 215 | end | ||
| 216 | |||
| 217 | # Places ActiveRecord-style validations on the size of the file assigned. The | ||
| 218 | # possible options are: | ||
| 219 | # * +in+: a Range of bytes (i.e. +1..1.megabyte+), | ||
| 220 | # * +less_than+: equivalent to :in => 0..options[:less_than] | ||
| 221 | # * +greater_than+: equivalent to :in => options[:greater_than]..Infinity | ||
| 222 | # * +message+: error message to display, use :min and :max as replacements | ||
| 223 | def validates_attachment_size name, options = {} | ||
| 224 | min = options[:greater_than] || (options[:in] && options[:in].first) || 0 | ||
| 225 | max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0) | ||
| 226 | range = (min..max) | ||
| 227 | message = options[:message] || "file size must be between :min and :max bytes." | ||
| 228 | |||
| 229 | attachment_definitions[name][:validations][:size] = lambda do |attachment, instance| | ||
| 230 | if attachment.file? && !range.include?(attachment.size.to_i) | ||
| 231 | message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s) | ||
| 232 | end | ||
| 233 | end | ||
| 234 | end | ||
| 235 | |||
| 236 | # Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true. | ||
| 237 | def validates_attachment_thumbnails name, options = {} | ||
| 238 | attachment_definitions[name][:whiny_thumbnails] = true | ||
| 239 | end | ||
| 240 | |||
| 241 | # Places ActiveRecord-style validations on the presence of a file. | ||
| 242 | def validates_attachment_presence name, options = {} | ||
| 243 | message = options[:message] || "must be set." | ||
| 244 | attachment_definitions[name][:validations][:presence] = lambda do |attachment, instance| | ||
| 245 | message unless attachment.file? | ||
| 246 | end | ||
| 247 | end | ||
| 248 | |||
| 249 | # Places ActiveRecord-style validations on the content type of the file | ||
| 250 | # assigned. The possible options are: | ||
| 251 | # * +content_type+: Allowed content types. Can be a single content type | ||
| 252 | # or an array. Each type can be a String or a Regexp. It should be | ||
| 253 | # noted that Internet Explorer upload files with content_types that you | ||
| 254 | # may not expect. For example, JPEG images are given image/pjpeg and | ||
| 255 | # PNGs are image/x-png, so keep that in mind when determining how you | ||
| 256 | # match. Allows all by default. | ||
| 257 | # * +message+: The message to display when the uploaded file has an invalid | ||
| 258 | # content type. | ||
| 259 | # NOTE: If you do not specify an [attachment]_content_type field on your | ||
| 260 | # model, content_type validation will work _ONLY upon assignment_ and | ||
| 261 | # re-validation after the instance has been reloaded will always succeed. | ||
| 262 | def validates_attachment_content_type name, options = {} | ||
| 263 | attachment_definitions[name][:validations][:content_type] = lambda do |attachment, instance| | ||
| 264 | valid_types = [options[:content_type]].flatten | ||
| 265 | |||
| 266 | unless attachment.original_filename.blank? | ||
| 267 | unless valid_types.blank? | ||
| 268 | content_type = attachment.instance_read(:content_type) | ||
| 269 | unless valid_types.any?{|t| content_type.nil? || t === content_type } | ||
| 270 | options[:message] || "is not one of the allowed file types." | ||
| 271 | end | ||
| 272 | end | ||
| 273 | end | ||
| 274 | end | ||
| 275 | end | ||
| 276 | |||
| 277 | # Returns the attachment definitions defined by each call to | ||
| 278 | # has_attached_file. | ||
| 279 | def attachment_definitions | ||
| 280 | read_inheritable_attribute(:attachment_definitions) | ||
| 281 | end | ||
| 282 | end | ||
| 283 | |||
| 284 | module InstanceMethods #:nodoc: | ||
| 285 | def attachment_for name | ||
| 286 | @_paperclip_attachments ||= {} | ||
| 287 | @_paperclip_attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name]) | ||
| 288 | end | ||
| 289 | |||
| 290 | def each_attachment | ||
| 291 | self.class.attachment_definitions.each do |name, definition| | ||
| 292 | yield(name, attachment_for(name)) | ||
| 293 | end | ||
| 294 | end | ||
| 295 | |||
| 296 | def save_attached_files | ||
| 297 | logger.info("[paperclip] Saving attachments.") | ||
| 298 | each_attachment do |name, attachment| | ||
| 299 | attachment.send(:save) | ||
| 300 | end | ||
| 301 | end | ||
| 302 | |||
| 303 | def destroy_attached_files | ||
| 304 | logger.info("[paperclip] Deleting attachments.") | ||
| 305 | each_attachment do |name, attachment| | ||
| 306 | attachment.send(:queue_existing_for_delete) | ||
| 307 | attachment.send(:flush_deletes) | ||
| 308 | end | ||
| 309 | end | ||
| 310 | end | ||
| 311 | |||
| 312 | end | ||
| 313 | |||
| 314 | # Set it all up. | ||
| 315 | if Object.const_defined?("ActiveRecord") | ||
| 316 | ActiveRecord::Base.send(:include, Paperclip) | ||
| 317 | File.send(:include, Paperclip::Upfile) | ||
| 318 | end | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/attachment.rb b/vendor/plugins/paperclip/lib/paperclip/attachment.rb new file mode 100644 index 0000000..897c67e --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/attachment.rb | |||
| @@ -0,0 +1,403 @@ | |||
| 1 | module Paperclip | ||
| 2 | # The Attachment class manages the files for a given attachment. It saves | ||
| 3 | # when the model saves, deletes when the model is destroyed, and processes | ||
| 4 | # the file upon assignment. | ||
| 5 | class Attachment | ||
| 6 | |||
| 7 | def self.default_options | ||
| 8 | @default_options ||= { | ||
| 9 | :url => "/system/:attachment/:id/:style/:basename.:extension", | ||
| 10 | :path => ":rails_root/public/system/:attachment/:id/:style/:basename.:extension", | ||
| 11 | :styles => {}, | ||
| 12 | :default_url => "/:attachment/:style/missing.png", | ||
| 13 | :default_style => :original, | ||
| 14 | :validations => {}, | ||
| 15 | :storage => :filesystem | ||
| 16 | } | ||
| 17 | end | ||
| 18 | |||
| 19 | attr_reader :name, :instance, :styles, :default_style, :convert_options, :queued_for_write | ||
| 20 | |||
| 21 | # Creates an Attachment object. +name+ is the name of the attachment, | ||
| 22 | # +instance+ is the ActiveRecord object instance it's attached to, and | ||
| 23 | # +options+ is the same as the hash passed to +has_attached_file+. | ||
| 24 | def initialize name, instance, options = {} | ||
| 25 | @name = name | ||
| 26 | @instance = instance | ||
| 27 | |||
| 28 | options = self.class.default_options.merge(options) | ||
| 29 | |||
| 30 | @url = options[:url] | ||
| 31 | @url = @url.call(self) if @url.is_a?(Proc) | ||
| 32 | @path = options[:path] | ||
| 33 | @path = @path.call(self) if @path.is_a?(Proc) | ||
| 34 | @styles = options[:styles] | ||
| 35 | @styles = @styles.call(self) if @styles.is_a?(Proc) | ||
| 36 | @default_url = options[:default_url] | ||
| 37 | @validations = options[:validations] | ||
| 38 | @default_style = options[:default_style] | ||
| 39 | @storage = options[:storage] | ||
| 40 | @whiny = options[:whiny_thumbnails] | ||
| 41 | @convert_options = options[:convert_options] || {} | ||
| 42 | @processors = options[:processors] || [:thumbnail] | ||
| 43 | @options = options | ||
| 44 | @queued_for_delete = [] | ||
| 45 | @queued_for_write = {} | ||
| 46 | @errors = {} | ||
| 47 | @validation_errors = nil | ||
| 48 | @dirty = false | ||
| 49 | |||
| 50 | normalize_style_definition | ||
| 51 | initialize_storage | ||
| 52 | end | ||
| 53 | |||
| 54 | # What gets called when you call instance.attachment = File. It clears | ||
| 55 | # errors, assigns attributes, processes the file, and runs validations. It | ||
| 56 | # also queues up the previous file for deletion, to be flushed away on | ||
| 57 | # #save of its host. In addition to form uploads, you can also assign | ||
| 58 | # another Paperclip attachment: | ||
| 59 | # new_user.avatar = old_user.avatar | ||
| 60 | # If the file that is assigned is not valid, the processing (i.e. | ||
| 61 | # thumbnailing, etc) will NOT be run. | ||
| 62 | def assign uploaded_file | ||
| 63 | %w(file_name).each do |field| | ||
| 64 | unless @instance.class.column_names.include?("#{name}_#{field}") | ||
| 65 | raise PaperclipError.new("#{@instance.class} model does not have required column '#{name}_#{field}'") | ||
| 66 | end | ||
| 67 | end | ||
| 68 | |||
| 69 | if uploaded_file.is_a?(Paperclip::Attachment) | ||
| 70 | uploaded_file = uploaded_file.to_file(:original) | ||
| 71 | close_uploaded_file = uploaded_file.respond_to?(:close) | ||
| 72 | end | ||
| 73 | |||
| 74 | return nil unless valid_assignment?(uploaded_file) | ||
| 75 | |||
| 76 | uploaded_file.binmode if uploaded_file.respond_to? :binmode | ||
| 77 | self.clear | ||
| 78 | |||
| 79 | return nil if uploaded_file.nil? | ||
| 80 | |||
| 81 | @queued_for_write[:original] = uploaded_file.to_tempfile | ||
| 82 | instance_write(:file_name, uploaded_file.original_filename.strip.gsub(/[^\w\d\.\-]+/, '_')) | ||
| 83 | instance_write(:content_type, uploaded_file.content_type.to_s.strip) | ||
| 84 | instance_write(:file_size, uploaded_file.size.to_i) | ||
| 85 | instance_write(:updated_at, Time.now) | ||
| 86 | |||
| 87 | @dirty = true | ||
| 88 | |||
| 89 | post_process if valid? | ||
| 90 | |||
| 91 | # Reset the file size if the original file was reprocessed. | ||
| 92 | instance_write(:file_size, @queued_for_write[:original].size.to_i) | ||
| 93 | ensure | ||
| 94 | uploaded_file.close if close_uploaded_file | ||
| 95 | validate | ||
| 96 | end | ||
| 97 | |||
| 98 | # Returns the public URL of the attachment, with a given style. Note that | ||
| 99 | # this does not necessarily need to point to a file that your web server | ||
| 100 | # can access and can point to an action in your app, if you need fine | ||
| 101 | # grained security. This is not recommended if you don't need the | ||
| 102 | # security, however, for performance reasons. set | ||
| 103 | # include_updated_timestamp to false if you want to stop the attachment | ||
| 104 | # update time appended to the url | ||
| 105 | def url style = default_style, include_updated_timestamp = true | ||
| 106 | url = original_filename.nil? ? interpolate(@default_url, style) : interpolate(@url, style) | ||
| 107 | include_updated_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url | ||
| 108 | end | ||
| 109 | |||
| 110 | # Returns the path of the attachment as defined by the :path option. If the | ||
| 111 | # file is stored in the filesystem the path refers to the path of the file | ||
| 112 | # on disk. If the file is stored in S3, the path is the "key" part of the | ||
| 113 | # URL, and the :bucket option refers to the S3 bucket. | ||
| 114 | def path style = nil #:nodoc: | ||
| 115 | original_filename.nil? ? nil : interpolate(@path, style) | ||
| 116 | end | ||
| 117 | |||
| 118 | # Alias to +url+ | ||
| 119 | def to_s style = nil | ||
| 120 | url(style) | ||
| 121 | end | ||
| 122 | |||
| 123 | # Returns true if there are no errors on this attachment. | ||
| 124 | def valid? | ||
| 125 | validate | ||
| 126 | errors.empty? | ||
| 127 | end | ||
| 128 | |||
| 129 | # Returns an array containing the errors on this attachment. | ||
| 130 | def errors | ||
| 131 | @errors | ||
| 132 | end | ||
| 133 | |||
| 134 | # Returns true if there are changes that need to be saved. | ||
| 135 | def dirty? | ||
| 136 | @dirty | ||
| 137 | end | ||
| 138 | |||
| 139 | # Saves the file, if there are no errors. If there are, it flushes them to | ||
| 140 | # the instance's errors and returns false, cancelling the save. | ||
| 141 | def save | ||
| 142 | if valid? | ||
| 143 | flush_deletes | ||
| 144 | flush_writes | ||
| 145 | @dirty = false | ||
| 146 | true | ||
| 147 | else | ||
| 148 | flush_errors | ||
| 149 | false | ||
| 150 | end | ||
| 151 | end | ||
| 152 | |||
| 153 | # Clears out the attachment. Has the same effect as previously assigning | ||
| 154 | # nil to the attachment. Does NOT save. If you wish to clear AND save, | ||
| 155 | # use #destroy. | ||
| 156 | def clear | ||
| 157 | queue_existing_for_delete | ||
| 158 | @errors = {} | ||
| 159 | @validation_errors = nil | ||
| 160 | end | ||
| 161 | |||
| 162 | # Destroys the attachment. Has the same effect as previously assigning | ||
| 163 | # nil to the attachment *and saving*. This is permanent. If you wish to | ||
| 164 | # wipe out the existing attachment but not save, use #clear. | ||
| 165 | def destroy | ||
| 166 | clear | ||
| 167 | save | ||
| 168 | end | ||
| 169 | |||
| 170 | # Returns the name of the file as originally assigned, and lives in the | ||
| 171 | # <attachment>_file_name attribute of the model. | ||
| 172 | def original_filename | ||
| 173 | instance_read(:file_name) | ||
| 174 | end | ||
| 175 | |||
| 176 | # Returns the size of the file as originally assigned, and lives in the | ||
| 177 | # <attachment>_file_size attribute of the model. | ||
| 178 | def size | ||
| 179 | instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size) | ||
| 180 | end | ||
| 181 | |||
| 182 | # Returns the content_type of the file as originally assigned, and lives | ||
| 183 | # in the <attachment>_content_type attribute of the model. | ||
| 184 | def content_type | ||
| 185 | instance_read(:content_type) | ||
| 186 | end | ||
| 187 | |||
| 188 | # Returns the last modified time of the file as originally assigned, and | ||
| 189 | # lives in the <attachment>_updated_at attribute of the model. | ||
| 190 | def updated_at | ||
| 191 | time = instance_read(:updated_at) | ||
| 192 | time && time.to_i | ||
| 193 | end | ||
| 194 | |||
| 195 | # A hash of procs that are run during the interpolation of a path or url. | ||
| 196 | # A variable of the format :name will be replaced with the return value of | ||
| 197 | # the proc named ":name". Each lambda takes the attachment and the current | ||
| 198 | # style as arguments. This hash can be added to with your own proc if | ||
| 199 | # necessary. | ||
| 200 | def self.interpolations | ||
| 201 | @interpolations ||= { | ||
| 202 | :rails_root => lambda{|attachment,style| RAILS_ROOT }, | ||
| 203 | :rails_env => lambda{|attachment,style| RAILS_ENV }, | ||
| 204 | :class => lambda do |attachment,style| | ||
| 205 | attachment.instance.class.name.underscore.pluralize | ||
| 206 | end, | ||
| 207 | :basename => lambda do |attachment,style| | ||
| 208 | attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "") | ||
| 209 | end, | ||
| 210 | :extension => lambda do |attachment,style| | ||
| 211 | ((style = attachment.styles[style]) && style[:format]) || | ||
| 212 | File.extname(attachment.original_filename).gsub(/^\.+/, "") | ||
| 213 | end, | ||
| 214 | :id => lambda{|attachment,style| attachment.instance.id }, | ||
| 215 | :id_partition => lambda do |attachment, style| | ||
| 216 | ("%09d" % attachment.instance.id).scan(/\d{3}/).join("/") | ||
| 217 | end, | ||
| 218 | :attachment => lambda{|attachment,style| attachment.name.to_s.downcase.pluralize }, | ||
| 219 | :style => lambda{|attachment,style| style || attachment.default_style }, | ||
| 220 | } | ||
| 221 | end | ||
| 222 | |||
| 223 | # This method really shouldn't be called that often. It's expected use is | ||
| 224 | # in the paperclip:refresh rake task and that's it. It will regenerate all | ||
| 225 | # thumbnails forcefully, by reobtaining the original file and going through | ||
| 226 | # the post-process again. | ||
| 227 | def reprocess! | ||
| 228 | new_original = Tempfile.new("paperclip-reprocess") | ||
| 229 | new_original.binmode | ||
| 230 | if old_original = to_file(:original) | ||
| 231 | new_original.write( old_original.read ) | ||
| 232 | new_original.rewind | ||
| 233 | |||
| 234 | @queued_for_write = { :original => new_original } | ||
| 235 | post_process | ||
| 236 | |||
| 237 | old_original.close if old_original.respond_to?(:close) | ||
| 238 | |||
| 239 | save | ||
| 240 | else | ||
| 241 | true | ||
| 242 | end | ||
| 243 | end | ||
| 244 | |||
| 245 | # Returns true if a file has been assigned. | ||
| 246 | def file? | ||
| 247 | !original_filename.blank? | ||
| 248 | end | ||
| 249 | |||
| 250 | # Writes the attachment-specific attribute on the instance. For example, | ||
| 251 | # instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's | ||
| 252 | # "avatar_file_name" field (assuming the attachment is called avatar). | ||
| 253 | def instance_write(attr, value) | ||
| 254 | setter = :"#{name}_#{attr}=" | ||
| 255 | responds = instance.respond_to?(setter) | ||
| 256 | self.instance_variable_set("@_#{setter.to_s.chop}", value) | ||
| 257 | instance.send(setter, value) if responds || attr.to_s == "file_name" | ||
| 258 | end | ||
| 259 | |||
| 260 | # Reads the attachment-specific attribute on the instance. See instance_write | ||
| 261 | # for more details. | ||
| 262 | def instance_read(attr) | ||
| 263 | getter = :"#{name}_#{attr}" | ||
| 264 | responds = instance.respond_to?(getter) | ||
| 265 | cached = self.instance_variable_get("@_#{getter}") | ||
| 266 | return cached if cached | ||
| 267 | instance.send(getter) if responds || attr.to_s == "file_name" | ||
| 268 | end | ||
| 269 | |||
| 270 | private | ||
| 271 | |||
| 272 | def logger #:nodoc: | ||
| 273 | instance.logger | ||
| 274 | end | ||
| 275 | |||
| 276 | def log message #:nodoc: | ||
| 277 | logger.info("[paperclip] #{message}") if logging? | ||
| 278 | end | ||
| 279 | |||
| 280 | def logging? #:nodoc: | ||
| 281 | Paperclip.options[:log] | ||
| 282 | end | ||
| 283 | |||
| 284 | def valid_assignment? file #:nodoc: | ||
| 285 | file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type)) | ||
| 286 | end | ||
| 287 | |||
| 288 | def validate #:nodoc: | ||
| 289 | unless @validation_errors | ||
| 290 | @validation_errors = @validations.inject({}) do |errors, validation| | ||
| 291 | name, block = validation | ||
| 292 | errors[name] = block.call(self, instance) if block | ||
| 293 | errors | ||
| 294 | end | ||
| 295 | @validation_errors.reject!{|k,v| v == nil } | ||
| 296 | @errors.merge!(@validation_errors) | ||
| 297 | end | ||
| 298 | @validation_errors | ||
| 299 | end | ||
| 300 | |||
| 301 | def normalize_style_definition #:nodoc: | ||
| 302 | @styles.each do |name, args| | ||
| 303 | unless args.is_a? Hash | ||
| 304 | dimensions, format = *args | ||
| 305 | @styles[name] = { | ||
| 306 | :processors => @processors, | ||
| 307 | :geometry => dimensions, | ||
| 308 | :format => format, | ||
| 309 | :whiny => @whiny, | ||
| 310 | :convert_options => extra_options_for(name) | ||
| 311 | } | ||
| 312 | else | ||
| 313 | @styles[name] = { | ||
| 314 | :processors => @processors, | ||
| 315 | :whiny => @whiny, | ||
| 316 | :convert_options => extra_options_for(name) | ||
| 317 | }.merge(@styles[name]) | ||
| 318 | end | ||
| 319 | end | ||
| 320 | end | ||
| 321 | |||
| 322 | def solidify_style_definitions #:nodoc: | ||
| 323 | @styles.each do |name, args| | ||
| 324 | @styles[name][:geometry] = @styles[name][:geometry].call(instance) if @styles[name][:geometry].respond_to?(:call) | ||
| 325 | @styles[name][:processors] = @styles[name][:processors].call(instance) if @styles[name][:processors].respond_to?(:call) | ||
| 326 | end | ||
| 327 | end | ||
| 328 | |||
| 329 | def initialize_storage #:nodoc: | ||
| 330 | @storage_module = Paperclip::Storage.const_get(@storage.to_s.capitalize) | ||
| 331 | self.extend(@storage_module) | ||
| 332 | end | ||
| 333 | |||
| 334 | def extra_options_for(style) #:nodoc: | ||
| 335 | all_options = convert_options[:all] | ||
| 336 | all_options = all_options.call(instance) if all_options.respond_to?(:call) | ||
| 337 | style_options = convert_options[style] | ||
| 338 | style_options = style_options.call(instance) if style_options.respond_to?(:call) | ||
| 339 | |||
| 340 | [ style_options, all_options ].compact.join(" ") | ||
| 341 | end | ||
| 342 | |||
| 343 | def post_process #:nodoc: | ||
| 344 | return if @queued_for_write[:original].nil? | ||
| 345 | solidify_style_definitions | ||
| 346 | return if fire_events(:before) | ||
| 347 | post_process_styles | ||
| 348 | return if fire_events(:after) | ||
| 349 | end | ||
| 350 | |||
| 351 | def fire_events(which) | ||
| 352 | return true if callback(:"#{which}_post_process") == false | ||
| 353 | return true if callback(:"#{which}_#{name}_post_process") == false | ||
| 354 | end | ||
| 355 | |||
| 356 | def callback which #:nodoc: | ||
| 357 | instance.run_callbacks(which, @queued_for_write){|result, obj| result == false } | ||
| 358 | end | ||
| 359 | |||
| 360 | def post_process_styles | ||
| 361 | @styles.each do |name, args| | ||
| 362 | begin | ||
| 363 | raise RuntimeError.new("Style #{name} has no processors defined.") if args[:processors].blank? | ||
| 364 | @queued_for_write[name] = args[:processors].inject(@queued_for_write[:original]) do |file, processor| | ||
| 365 | Paperclip.processor(processor).make(file, args, self) | ||
| 366 | end | ||
| 367 | rescue PaperclipError => e | ||
| 368 | log("An error was received while processing: #{e.inspect}") | ||
| 369 | (@errors[:processing] ||= []) << e.message if @whiny | ||
| 370 | end | ||
| 371 | end | ||
| 372 | end | ||
| 373 | |||
| 374 | def interpolate pattern, style = default_style #:nodoc: | ||
| 375 | interpolations = self.class.interpolations.sort{|a,b| a.first.to_s <=> b.first.to_s } | ||
| 376 | interpolations.reverse.inject( pattern.dup ) do |result, interpolation| | ||
| 377 | tag, blk = interpolation | ||
| 378 | result.gsub(/:#{tag}/) do |match| | ||
| 379 | blk.call( self, style ) | ||
| 380 | end | ||
| 381 | end | ||
| 382 | end | ||
| 383 | |||
| 384 | def queue_existing_for_delete #:nodoc: | ||
| 385 | return unless file? | ||
| 386 | @queued_for_delete += [:original, *@styles.keys].uniq.map do |style| | ||
| 387 | path(style) if exists?(style) | ||
| 388 | end.compact | ||
| 389 | instance_write(:file_name, nil) | ||
| 390 | instance_write(:content_type, nil) | ||
| 391 | instance_write(:file_size, nil) | ||
| 392 | instance_write(:updated_at, nil) | ||
| 393 | end | ||
| 394 | |||
| 395 | def flush_errors #:nodoc: | ||
| 396 | @errors.each do |error, message| | ||
| 397 | [message].flatten.each {|m| instance.errors.add(name, m) } | ||
| 398 | end | ||
| 399 | end | ||
| 400 | |||
| 401 | end | ||
| 402 | end | ||
| 403 | |||
diff --git a/vendor/plugins/paperclip/lib/paperclip/callback_compatability.rb b/vendor/plugins/paperclip/lib/paperclip/callback_compatability.rb new file mode 100644 index 0000000..10b08fc --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/callback_compatability.rb | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | module Paperclip | ||
| 2 | # This module is intended as a compatability shim for the differences in | ||
| 3 | # callbacks between Rails 2.0 and Rails 2.1. | ||
| 4 | module CallbackCompatability | ||
| 5 | def self.included(base) | ||
| 6 | base.extend(ClassMethods) | ||
| 7 | base.send(:include, InstanceMethods) | ||
| 8 | end | ||
| 9 | |||
| 10 | module ClassMethods | ||
| 11 | # The implementation of this method is taken from the Rails 1.2.6 source, | ||
| 12 | # from rails/activerecord/lib/active_record/callbacks.rb, line 192. | ||
| 13 | def define_callbacks(*args) | ||
| 14 | args.each do |method| | ||
| 15 | self.class_eval <<-"end_eval" | ||
| 16 | def self.#{method}(*callbacks, &block) | ||
| 17 | callbacks << block if block_given? | ||
| 18 | write_inheritable_array(#{method.to_sym.inspect}, callbacks) | ||
| 19 | end | ||
| 20 | end_eval | ||
| 21 | end | ||
| 22 | end | ||
| 23 | end | ||
| 24 | |||
| 25 | module InstanceMethods | ||
| 26 | # The callbacks in < 2.1 don't worry about the extra options or the | ||
| 27 | # block, so just run what we have available. | ||
| 28 | def run_callbacks(meth, opts = nil, &blk) | ||
| 29 | callback(meth) | ||
| 30 | end | ||
| 31 | end | ||
| 32 | end | ||
| 33 | end | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/geometry.rb b/vendor/plugins/paperclip/lib/paperclip/geometry.rb new file mode 100644 index 0000000..7fbe038 --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/geometry.rb | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | module Paperclip | ||
| 2 | |||
| 3 | # Defines the geometry of an image. | ||
| 4 | class Geometry | ||
| 5 | attr_accessor :height, :width, :modifier | ||
| 6 | |||
| 7 | # Gives a Geometry representing the given height and width | ||
| 8 | def initialize width = nil, height = nil, modifier = nil | ||
| 9 | @height = height.to_f | ||
| 10 | @width = width.to_f | ||
| 11 | @modifier = modifier | ||
| 12 | end | ||
| 13 | |||
| 14 | # Uses ImageMagick to determing the dimensions of a file, passed in as either a | ||
| 15 | # File or path. | ||
| 16 | def self.from_file file | ||
| 17 | file = file.path if file.respond_to? "path" | ||
| 18 | geometry = begin | ||
| 19 | Paperclip.run("identify", %Q[-format "%wx%h" "#{file}"[0]]) | ||
| 20 | rescue PaperclipCommandLineError | ||
| 21 | "" | ||
| 22 | end | ||
| 23 | parse(geometry) || | ||
| 24 | raise(NotIdentifiedByImageMagickError.new("#{file} is not recognized by the 'identify' command.")) | ||
| 25 | end | ||
| 26 | |||
| 27 | # Parses a "WxH" formatted string, where W is the width and H is the height. | ||
| 28 | def self.parse string | ||
| 29 | if match = (string && string.match(/\b(\d*)x?(\d*)\b([\>\<\#\@\%^!])?/)) | ||
| 30 | Geometry.new(*match[1,3]) | ||
| 31 | end | ||
| 32 | end | ||
| 33 | |||
| 34 | # True if the dimensions represent a square | ||
| 35 | def square? | ||
| 36 | height == width | ||
| 37 | end | ||
| 38 | |||
| 39 | # True if the dimensions represent a horizontal rectangle | ||
| 40 | def horizontal? | ||
| 41 | height < width | ||
| 42 | end | ||
| 43 | |||
| 44 | # True if the dimensions represent a vertical rectangle | ||
| 45 | def vertical? | ||
| 46 | height > width | ||
| 47 | end | ||
| 48 | |||
| 49 | # The aspect ratio of the dimensions. | ||
| 50 | def aspect | ||
| 51 | width / height | ||
| 52 | end | ||
| 53 | |||
| 54 | # Returns the larger of the two dimensions | ||
| 55 | def larger | ||
| 56 | [height, width].max | ||
| 57 | end | ||
| 58 | |||
| 59 | # Returns the smaller of the two dimensions | ||
| 60 | def smaller | ||
| 61 | [height, width].min | ||
| 62 | end | ||
| 63 | |||
| 64 | # Returns the width and height in a format suitable to be passed to Geometry.parse | ||
| 65 | def to_s | ||
| 66 | s = "" | ||
| 67 | s << width.to_i.to_s if width > 0 | ||
| 68 | s << "x#{height.to_i}" if height > 0 | ||
| 69 | s << modifier.to_s | ||
| 70 | s | ||
| 71 | end | ||
| 72 | |||
| 73 | # Same as to_s | ||
| 74 | def inspect | ||
| 75 | to_s | ||
| 76 | end | ||
| 77 | |||
| 78 | # Returns the scaling and cropping geometries (in string-based ImageMagick format) | ||
| 79 | # neccessary to transform this Geometry into the Geometry given. If crop is true, | ||
| 80 | # then it is assumed the destination Geometry will be the exact final resolution. | ||
| 81 | # In this case, the source Geometry is scaled so that an image containing the | ||
| 82 | # destination Geometry would be completely filled by the source image, and any | ||
| 83 | # overhanging image would be cropped. Useful for square thumbnail images. The cropping | ||
| 84 | # is weighted at the center of the Geometry. | ||
| 85 | def transformation_to dst, crop = false | ||
| 86 | if crop | ||
| 87 | ratio = Geometry.new( dst.width / self.width, dst.height / self.height ) | ||
| 88 | scale_geometry, scale = scaling(dst, ratio) | ||
| 89 | crop_geometry = cropping(dst, ratio, scale) | ||
| 90 | else | ||
| 91 | scale_geometry = dst.to_s | ||
| 92 | end | ||
| 93 | |||
| 94 | [ scale_geometry, crop_geometry ] | ||
| 95 | end | ||
| 96 | |||
| 97 | private | ||
| 98 | |||
| 99 | def scaling dst, ratio | ||
| 100 | if ratio.horizontal? || ratio.square? | ||
| 101 | [ "%dx" % dst.width, ratio.width ] | ||
| 102 | else | ||
| 103 | [ "x%d" % dst.height, ratio.height ] | ||
| 104 | end | ||
| 105 | end | ||
| 106 | |||
| 107 | def cropping dst, ratio, scale | ||
| 108 | if ratio.horizontal? || ratio.square? | ||
| 109 | "%dx%d+%d+%d" % [ dst.width, dst.height, 0, (self.height * scale - dst.height) / 2 ] | ||
| 110 | else | ||
| 111 | "%dx%d+%d+%d" % [ dst.width, dst.height, (self.width * scale - dst.width) / 2, 0 ] | ||
| 112 | end | ||
| 113 | end | ||
| 114 | end | ||
| 115 | end | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/iostream.rb b/vendor/plugins/paperclip/lib/paperclip/iostream.rb new file mode 100644 index 0000000..74e2014 --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/iostream.rb | |||
| @@ -0,0 +1,58 @@ | |||
| 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 | # 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 #:nodoc: | ||
| 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 | ||
| 44 | |||
| 45 | # Corrects a bug in Windows when asking for Tempfile size. | ||
| 46 | if defined? Tempfile | ||
| 47 | class Tempfile | ||
| 48 | def size | ||
| 49 | if @tmpfile | ||
| 50 | @tmpfile.fsync | ||
| 51 | @tmpfile.flush | ||
| 52 | @tmpfile.stat.size | ||
| 53 | else | ||
| 54 | 0 | ||
| 55 | end | ||
| 56 | end | ||
| 57 | end | ||
| 58 | end | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers.rb b/vendor/plugins/paperclip/lib/paperclip/matchers.rb new file mode 100644 index 0000000..ca24b5e --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/matchers.rb | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | require 'paperclip/matchers/have_attached_file_matcher' | ||
| 2 | require 'paperclip/matchers/validate_attachment_presence_matcher' | ||
| 3 | require 'paperclip/matchers/validate_attachment_content_type_matcher' | ||
| 4 | require 'paperclip/matchers/validate_attachment_size_matcher' | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers/have_attached_file_matcher.rb b/vendor/plugins/paperclip/lib/paperclip/matchers/have_attached_file_matcher.rb new file mode 100644 index 0000000..da0dd8b --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/matchers/have_attached_file_matcher.rb | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | module Paperclip | ||
| 2 | module Shoulda | ||
| 3 | module Matchers | ||
| 4 | def have_attached_file name | ||
| 5 | HaveAttachedFileMatcher.new(name) | ||
| 6 | end | ||
| 7 | |||
| 8 | class HaveAttachedFileMatcher | ||
| 9 | def initialize attachment_name | ||
| 10 | @attachment_name = attachment_name | ||
| 11 | end | ||
| 12 | |||
| 13 | def matches? subject | ||
| 14 | @subject = subject | ||
| 15 | responds? && has_column? && included? | ||
| 16 | end | ||
| 17 | |||
| 18 | def failure_message | ||
| 19 | "Should have an attachment named #{@attachment_name}" | ||
| 20 | end | ||
| 21 | |||
| 22 | def negative_failure_message | ||
| 23 | "Should not have an attachment named #{@attachment_name}" | ||
| 24 | end | ||
| 25 | |||
| 26 | def description | ||
| 27 | "have an attachment named #{@attachment_name}" | ||
| 28 | end | ||
| 29 | |||
| 30 | protected | ||
| 31 | |||
| 32 | def responds? | ||
| 33 | methods = @subject.instance_methods | ||
| 34 | methods.include?("#{@attachment_name}") && | ||
| 35 | methods.include?("#{@attachment_name}=") && | ||
| 36 | methods.include?("#{@attachment_name}?") | ||
| 37 | end | ||
| 38 | |||
| 39 | def has_column? | ||
| 40 | @subject.column_names.include?("#{@attachment_name}_file_name") | ||
| 41 | end | ||
| 42 | |||
| 43 | def included? | ||
| 44 | @subject.ancestors.include?(Paperclip::InstanceMethods) | ||
| 45 | end | ||
| 46 | end | ||
| 47 | end | ||
| 48 | end | ||
| 49 | end | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb new file mode 100644 index 0000000..b4e97fd --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | module Paperclip | ||
| 2 | module Shoulda | ||
| 3 | module Matchers | ||
| 4 | def validate_attachment_content_type name | ||
| 5 | ValidateAttachmentContentTypeMatcher.new(name) | ||
| 6 | end | ||
| 7 | |||
| 8 | class ValidateAttachmentContentTypeMatcher | ||
| 9 | def initialize attachment_name | ||
| 10 | @attachment_name = attachment_name | ||
| 11 | end | ||
| 12 | |||
| 13 | def allowing *types | ||
| 14 | @allowed_types = types.flatten | ||
| 15 | self | ||
| 16 | end | ||
| 17 | |||
| 18 | def rejecting *types | ||
| 19 | @rejected_types = types.flatten | ||
| 20 | self | ||
| 21 | end | ||
| 22 | |||
| 23 | def matches? subject | ||
| 24 | @subject = subject | ||
| 25 | @allowed_types && @rejected_types && | ||
| 26 | allowed_types_allowed? && rejected_types_rejected? | ||
| 27 | end | ||
| 28 | |||
| 29 | def failure_message | ||
| 30 | "Content types #{@allowed_types.join(", ")} should be accepted" + | ||
| 31 | " and #{@rejected_types.join(", ")} rejected by #{@attachment_name}" | ||
| 32 | end | ||
| 33 | |||
| 34 | def negative_failure_message | ||
| 35 | "Content types #{@allowed_types.join(", ")} should be rejected" + | ||
| 36 | " and #{@rejected_types.join(", ")} accepted by #{@attachment_name}" | ||
| 37 | end | ||
| 38 | |||
| 39 | def description | ||
| 40 | "validate the content types allowed on attachment #{@attachment_name}" | ||
| 41 | end | ||
| 42 | |||
| 43 | protected | ||
| 44 | |||
| 45 | def allow_types?(types) | ||
| 46 | types.all? do |type| | ||
| 47 | file = StringIO.new(".") | ||
| 48 | file.content_type = type | ||
| 49 | attachment = @subject.new.attachment_for(@attachment_name) | ||
| 50 | attachment.assign(file) | ||
| 51 | attachment.errors[:content_type].nil? | ||
| 52 | end | ||
| 53 | end | ||
| 54 | |||
| 55 | def allowed_types_allowed? | ||
| 56 | allow_types?(@allowed_types) | ||
| 57 | end | ||
| 58 | |||
| 59 | def rejected_types_rejected? | ||
| 60 | not allow_types?(@rejected_types) | ||
| 61 | end | ||
| 62 | end | ||
| 63 | end | ||
| 64 | end | ||
| 65 | end | ||
| 66 | |||
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_presence_matcher.rb b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_presence_matcher.rb new file mode 100644 index 0000000..61dc0ea --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_presence_matcher.rb | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | module Paperclip | ||
| 2 | module Shoulda | ||
| 3 | module Matchers | ||
| 4 | def validate_attachment_presence name | ||
| 5 | ValidateAttachmentPresenceMatcher.new(name) | ||
| 6 | end | ||
| 7 | |||
| 8 | class ValidateAttachmentPresenceMatcher | ||
| 9 | def initialize attachment_name | ||
| 10 | @attachment_name = attachment_name | ||
| 11 | end | ||
| 12 | |||
| 13 | def matches? subject | ||
| 14 | @subject = subject | ||
| 15 | error_when_not_valid? && no_error_when_valid? | ||
| 16 | end | ||
| 17 | |||
| 18 | def failure_message | ||
| 19 | "Attachment #{@attachment_name} should be required" | ||
| 20 | end | ||
| 21 | |||
| 22 | def negative_failure_message | ||
| 23 | "Attachment #{@attachment_name} should not be required" | ||
| 24 | end | ||
| 25 | |||
| 26 | def description | ||
| 27 | "require presence of attachment #{@attachment_name}" | ||
| 28 | end | ||
| 29 | |||
| 30 | protected | ||
| 31 | |||
| 32 | def error_when_not_valid? | ||
| 33 | @attachment = @subject.new.send(@attachment_name) | ||
| 34 | @attachment.assign(nil) | ||
| 35 | not @attachment.errors[:presence].nil? | ||
| 36 | end | ||
| 37 | |||
| 38 | def no_error_when_valid? | ||
| 39 | @file = StringIO.new(".") | ||
| 40 | @attachment = @subject.new.send(@attachment_name) | ||
| 41 | @attachment.assign(@file) | ||
| 42 | @attachment.errors[:presence].nil? | ||
| 43 | end | ||
| 44 | end | ||
| 45 | end | ||
| 46 | end | ||
| 47 | end | ||
| 48 | |||
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_size_matcher.rb b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_size_matcher.rb new file mode 100644 index 0000000..f84c479 --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_size_matcher.rb | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | module Paperclip | ||
| 2 | module Shoulda | ||
| 3 | module Matchers | ||
| 4 | def validate_attachment_size name | ||
| 5 | ValidateAttachmentSizeMatcher.new(name) | ||
| 6 | end | ||
| 7 | |||
| 8 | class ValidateAttachmentSizeMatcher | ||
| 9 | def initialize attachment_name | ||
| 10 | @attachment_name = attachment_name | ||
| 11 | @low, @high = 0, (1.0/0) | ||
| 12 | end | ||
| 13 | |||
| 14 | def less_than size | ||
| 15 | @high = size | ||
| 16 | self | ||
| 17 | end | ||
| 18 | |||
| 19 | def greater_than size | ||
| 20 | @low = size | ||
| 21 | self | ||
| 22 | end | ||
| 23 | |||
| 24 | def in range | ||
| 25 | @low, @high = range.first, range.last | ||
| 26 | self | ||
| 27 | end | ||
| 28 | |||
| 29 | def matches? subject | ||
| 30 | @subject = subject | ||
| 31 | lower_than_low? && higher_than_low? && lower_than_high? && higher_than_high? | ||
| 32 | end | ||
| 33 | |||
| 34 | def failure_message | ||
| 35 | "Attachment #{@attachment_name} must be between #{@low} and #{@high} bytes" | ||
| 36 | end | ||
| 37 | |||
| 38 | def negative_failure_message | ||
| 39 | "Attachment #{@attachment_name} cannot be between #{@low} and #{@high} bytes" | ||
| 40 | end | ||
| 41 | |||
| 42 | def description | ||
| 43 | "validate the size of attachment #{@attachment_name}" | ||
| 44 | end | ||
| 45 | |||
| 46 | protected | ||
| 47 | |||
| 48 | def override_method object, method, &replacement | ||
| 49 | (class << object; self; end).class_eval do | ||
| 50 | define_method(method, &replacement) | ||
| 51 | end | ||
| 52 | end | ||
| 53 | |||
| 54 | def passes_validation_with_size(new_size) | ||
| 55 | file = StringIO.new(".") | ||
| 56 | override_method(file, :size){ new_size } | ||
| 57 | attachment = @subject.new.attachment_for(@attachment_name) | ||
| 58 | attachment.assign(file) | ||
| 59 | attachment.errors[:size].nil? | ||
| 60 | end | ||
| 61 | |||
| 62 | def lower_than_low? | ||
| 63 | not passes_validation_with_size(@low - 1) | ||
| 64 | end | ||
| 65 | |||
| 66 | def higher_than_low? | ||
| 67 | passes_validation_with_size(@low + 1) | ||
| 68 | end | ||
| 69 | |||
| 70 | def lower_than_high? | ||
| 71 | return true if @high == (1.0/0) | ||
| 72 | passes_validation_with_size(@high - 1) | ||
| 73 | end | ||
| 74 | |||
| 75 | def higher_than_high? | ||
| 76 | return true if @high == (1.0/0) | ||
| 77 | not passes_validation_with_size(@high + 1) | ||
| 78 | end | ||
| 79 | end | ||
| 80 | end | ||
| 81 | end | ||
| 82 | end | ||
| 83 | |||
diff --git a/vendor/plugins/paperclip/lib/paperclip/processor.rb b/vendor/plugins/paperclip/lib/paperclip/processor.rb new file mode 100644 index 0000000..9082bfd --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/processor.rb | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | module Paperclip | ||
| 2 | # Paperclip processors allow you to modify attached files when they are | ||
| 3 | # attached in any way you are able. Paperclip itself uses command-line | ||
| 4 | # programs for its included Thumbnail processor, but custom processors | ||
| 5 | # are not required to follow suit. | ||
| 6 | # | ||
| 7 | # Processors are required to be defined inside the Paperclip module and | ||
| 8 | # are also required to be a subclass of Paperclip::Processor. There are | ||
| 9 | # only two methods you must implement to properly be a subclass: | ||
| 10 | # #initialize and #make. Initialize's arguments are the file that will | ||
| 11 | # be operated on (which is an instance of File), and a hash of options | ||
| 12 | # that were defined in has_attached_file's style hash. | ||
| 13 | # | ||
| 14 | # All #make needs to do is return an instance of File (Tempfile is | ||
| 15 | # acceptable) which contains the results of the processing. | ||
| 16 | # | ||
| 17 | # See Paperclip.run for more information about using command-line | ||
| 18 | # utilities from within Processors. | ||
| 19 | class Processor | ||
| 20 | attr_accessor :file, :options, :attachment | ||
| 21 | |||
| 22 | def initialize file, options = {}, attachment = nil | ||
| 23 | @file = file | ||
| 24 | @options = options | ||
| 25 | @attachment = attachment | ||
| 26 | end | ||
| 27 | |||
| 28 | def make | ||
| 29 | end | ||
| 30 | |||
| 31 | def self.make file, options = {}, attachment = nil | ||
| 32 | new(file, options, attachment).make | ||
| 33 | end | ||
| 34 | end | ||
| 35 | |||
| 36 | # Due to how ImageMagick handles its image format conversion and how Tempfile | ||
| 37 | # handles its naming scheme, it is necessary to override how Tempfile makes | ||
| 38 | # its names so as to allow for file extensions. Idea taken from the comments | ||
| 39 | # on this blog post: | ||
| 40 | # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions | ||
| 41 | class Tempfile < ::Tempfile | ||
| 42 | # Replaces Tempfile's +make_tmpname+ with one that honors file extensions. | ||
| 43 | def make_tmpname(basename, n) | ||
| 44 | extension = File.extname(basename) | ||
| 45 | sprintf("%s,%d,%d%s", File.basename(basename, extension), $$, n, extension) | ||
| 46 | end | ||
| 47 | end | ||
| 48 | end | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/storage.rb b/vendor/plugins/paperclip/lib/paperclip/storage.rb new file mode 100644 index 0000000..58913ad --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/storage.rb | |||
| @@ -0,0 +1,236 @@ | |||
| 1 | module Paperclip | ||
| 2 | module Storage | ||
| 3 | |||
| 4 | # The default place to store attachments is in the filesystem. Files on the local | ||
| 5 | # filesystem can be very easily served by Apache without requiring a hit to your app. | ||
| 6 | # They also can be processed more easily after they've been saved, as they're just | ||
| 7 | # normal files. There is one Filesystem-specific option for has_attached_file. | ||
| 8 | # * +path+: The location of the repository of attachments on disk. This can (and, in | ||
| 9 | # almost all cases, should) be coordinated with the value of the +url+ option to | ||
| 10 | # allow files to be saved into a place where Apache can serve them without | ||
| 11 | # hitting your app. Defaults to | ||
| 12 | # ":rails_root/public/:attachment/:id/:style/:basename.:extension" | ||
| 13 | # By default this places the files in the app's public directory which can be served | ||
| 14 | # directly. If you are using capistrano for deployment, a good idea would be to | ||
| 15 | # make a symlink to the capistrano-created system directory from inside your app's | ||
| 16 | # public directory. | ||
| 17 | # See Paperclip::Attachment#interpolate for more information on variable interpolaton. | ||
| 18 | # :path => "/var/app/attachments/:class/:id/:style/:basename.:extension" | ||
| 19 | module Filesystem | ||
| 20 | def self.extended base | ||
| 21 | end | ||
| 22 | |||
| 23 | def exists?(style = default_style) | ||
| 24 | if original_filename | ||
| 25 | File.exist?(path(style)) | ||
| 26 | else | ||
| 27 | false | ||
| 28 | end | ||
| 29 | end | ||
| 30 | |||
| 31 | # Returns representation of the data of the file assigned to the given | ||
| 32 | # style, in the format most representative of the current storage. | ||
| 33 | def to_file style = default_style | ||
| 34 | @queued_for_write[style] || (File.new(path(style), 'rb') if exists?(style)) | ||
| 35 | end | ||
| 36 | alias_method :to_io, :to_file | ||
| 37 | |||
| 38 | def flush_writes #:nodoc: | ||
| 39 | @queued_for_write.each do |style, file| | ||
| 40 | file.close | ||
| 41 | FileUtils.mkdir_p(File.dirname(path(style))) | ||
| 42 | logger.info("[paperclip] saving #{path(style)}") | ||
| 43 | FileUtils.mv(file.path, path(style)) | ||
| 44 | FileUtils.chmod(0644, path(style)) | ||
| 45 | end | ||
| 46 | @queued_for_write = {} | ||
| 47 | end | ||
| 48 | |||
| 49 | def flush_deletes #:nodoc: | ||
| 50 | @queued_for_delete.each do |path| | ||
| 51 | begin | ||
| 52 | logger.info("[paperclip] deleting #{path}") | ||
| 53 | FileUtils.rm(path) if File.exist?(path) | ||
| 54 | rescue Errno::ENOENT => e | ||
| 55 | # ignore file-not-found, let everything else pass | ||
| 56 | end | ||
| 57 | begin | ||
| 58 | while(true) | ||
| 59 | path = File.dirname(path) | ||
| 60 | FileUtils.rmdir(path) | ||
| 61 | end | ||
| 62 | rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR | ||
| 63 | # Stop trying to remove parent directories | ||
| 64 | rescue SystemCallError => e | ||
| 65 | logger.info("[paperclip] There was an unexpected error while deleting directories: #{e.class}") | ||
| 66 | # Ignore it | ||
| 67 | end | ||
| 68 | end | ||
| 69 | @queued_for_delete = [] | ||
| 70 | end | ||
| 71 | end | ||
| 72 | |||
| 73 | # Amazon's S3 file hosting service is a scalable, easy place to store files for | ||
| 74 | # distribution. You can find out more about it at http://aws.amazon.com/s3 | ||
| 75 | # There are a few S3-specific options for has_attached_file: | ||
| 76 | # * +s3_credentials+: Takes a path, a File, or a Hash. The path (or File) must point | ||
| 77 | # to a YAML file containing the +access_key_id+ and +secret_access_key+ that Amazon | ||
| 78 | # gives you. You can 'environment-space' this just like you do to your | ||
| 79 | # database.yml file, so different environments can use different accounts: | ||
| 80 | # development: | ||
| 81 | # access_key_id: 123... | ||
| 82 | # secret_access_key: 123... | ||
| 83 | # test: | ||
| 84 | # access_key_id: abc... | ||
| 85 | # secret_access_key: abc... | ||
| 86 | # production: | ||
| 87 | # access_key_id: 456... | ||
| 88 | # secret_access_key: 456... | ||
| 89 | # This is not required, however, and the file may simply look like this: | ||
| 90 | # access_key_id: 456... | ||
| 91 | # secret_access_key: 456... | ||
| 92 | # In which case, those access keys will be used in all environments. You can also | ||
| 93 | # put your bucket name in this file, instead of adding it to the code directly. | ||
| 94 | # This is useful when you want the same account but a different bucket for | ||
| 95 | # development versus production. | ||
| 96 | # * +s3_permissions+: This is a String that should be one of the "canned" access | ||
| 97 | # policies that S3 provides (more information can be found here: | ||
| 98 | # http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html#RESTCannedAccessPolicies) | ||
| 99 | # The default for Paperclip is "public-read". | ||
| 100 | # * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either | ||
| 101 | # 'http' or 'https'. Defaults to 'http' when your :s3_permissions are 'public-read' (the | ||
| 102 | # default), and 'https' when your :s3_permissions are anything else. | ||
| 103 | # * +s3_headers+: A hash of headers such as {'Expires' => 1.year.from_now.httpdate} | ||
| 104 | # * +bucket+: This is the name of the S3 bucket that will store your files. Remember | ||
| 105 | # that the bucket must be unique across all of Amazon S3. If the bucket does not exist | ||
| 106 | # Paperclip will attempt to create it. The bucket name will not be interpolated. | ||
| 107 | # You can define the bucket as a Proc if you want to determine it's name at runtime. | ||
| 108 | # Paperclip will call that Proc with attachment as the only argument. | ||
| 109 | # * +s3_host_alias+: The fully-qualified domain name (FQDN) that is the alias to the | ||
| 110 | # S3 domain of your bucket. Used with the :s3_alias_url url interpolation. See the | ||
| 111 | # link in the +url+ entry for more information about S3 domains and buckets. | ||
| 112 | # * +url+: There are three options for the S3 url. You can choose to have the bucket's name | ||
| 113 | # placed domain-style (bucket.s3.amazonaws.com) or path-style (s3.amazonaws.com/bucket). | ||
| 114 | # Lastly, you can specify a CNAME (which requires the CNAME to be specified as | ||
| 115 | # :s3_alias_url. You can read more about CNAMEs and S3 at | ||
| 116 | # http://docs.amazonwebservices.com/AmazonS3/latest/index.html?VirtualHosting.html | ||
| 117 | # Normally, this won't matter in the slightest and you can leave the default (which is | ||
| 118 | # path-style, or :s3_path_url). But in some cases paths don't work and you need to use | ||
| 119 | # the domain-style (:s3_domain_url). Anything else here will be treated like path-style. | ||
| 120 | # NOTE: If you use a CNAME for use with CloudFront, you can NOT specify https as your | ||
| 121 | # :s3_protocol; This is *not supported* by S3/CloudFront. Finally, when using the host | ||
| 122 | # alias, the :bucket parameter is ignored, as the hostname is used as the bucket name | ||
| 123 | # by S3. | ||
| 124 | # * +path+: This is the key under the bucket in which the file will be stored. The | ||
| 125 | # URL will be constructed from the bucket and the path. This is what you will want | ||
| 126 | # to interpolate. Keys should be unique, like filenames, and despite the fact that | ||
| 127 | # S3 (strictly speaking) does not support directories, you can still use a / to | ||
| 128 | # separate parts of your file name. | ||
| 129 | module S3 | ||
| 130 | def self.extended base | ||
| 131 | require 'right_aws' | ||
| 132 | base.instance_eval do | ||
| 133 | @s3_credentials = parse_credentials(@options[:s3_credentials]) | ||
| 134 | @bucket = @options[:bucket] || @s3_credentials[:bucket] | ||
| 135 | @bucket = @bucket.call(self) if @bucket.is_a?(Proc) | ||
| 136 | @s3_options = @options[:s3_options] || {} | ||
| 137 | @s3_permissions = @options[:s3_permissions] || 'public-read' | ||
| 138 | @s3_protocol = @options[:s3_protocol] || (@s3_permissions == 'public-read' ? 'http' : 'https') | ||
| 139 | @s3_headers = @options[:s3_headers] || {} | ||
| 140 | @s3_host_alias = @options[:s3_host_alias] | ||
| 141 | @url = ":s3_path_url" unless @url.to_s.match(/^:s3.*url$/) | ||
| 142 | end | ||
| 143 | base.class.interpolations[:s3_alias_url] = lambda do |attachment, style| | ||
| 144 | "#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}" | ||
| 145 | end | ||
| 146 | base.class.interpolations[:s3_path_url] = lambda do |attachment, style| | ||
| 147 | "#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}" | ||
| 148 | end | ||
| 149 | base.class.interpolations[:s3_domain_url] = lambda do |attachment, style| | ||
| 150 | "#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}" | ||
| 151 | end | ||
| 152 | end | ||
| 153 | |||
| 154 | def s3 | ||
| 155 | @s3 ||= RightAws::S3.new(@s3_credentials[:access_key_id], | ||
| 156 | @s3_credentials[:secret_access_key], | ||
| 157 | @s3_options) | ||
| 158 | end | ||
| 159 | |||
| 160 | def s3_bucket | ||
| 161 | @s3_bucket ||= s3.bucket(@bucket, true, @s3_permissions) | ||
| 162 | end | ||
| 163 | |||
| 164 | def bucket_name | ||
| 165 | @bucket | ||
| 166 | end | ||
| 167 | |||
| 168 | def s3_host_alias | ||
| 169 | @s3_host_alias | ||
| 170 | end | ||
| 171 | |||
| 172 | def parse_credentials creds | ||
| 173 | creds = find_credentials(creds).stringify_keys | ||
| 174 | (creds[ENV['RAILS_ENV']] || creds).symbolize_keys | ||
| 175 | end | ||
| 176 | |||
| 177 | def exists?(style = default_style) | ||
| 178 | s3_bucket.key(path(style)) ? true : false | ||
| 179 | end | ||
| 180 | |||
| 181 | def s3_protocol | ||
| 182 | @s3_protocol | ||
| 183 | end | ||
| 184 | |||
| 185 | # Returns representation of the data of the file assigned to the given | ||
| 186 | # style, in the format most representative of the current storage. | ||
| 187 | def to_file style = default_style | ||
| 188 | @queued_for_write[style] || s3_bucket.key(path(style)) | ||
| 189 | end | ||
| 190 | alias_method :to_io, :to_file | ||
| 191 | |||
| 192 | def flush_writes #:nodoc: | ||
| 193 | @queued_for_write.each do |style, file| | ||
| 194 | begin | ||
| 195 | logger.info("[paperclip] saving #{path(style)}") | ||
| 196 | key = s3_bucket.key(path(style)) | ||
| 197 | key.data = file | ||
| 198 | key.put(nil, @s3_permissions, {'Content-type' => instance_read(:content_type)}.merge(@s3_headers)) | ||
| 199 | rescue RightAws::AwsError => e | ||
| 200 | raise | ||
| 201 | end | ||
| 202 | end | ||
| 203 | @queued_for_write = {} | ||
| 204 | end | ||
| 205 | |||
| 206 | def flush_deletes #:nodoc: | ||
| 207 | @queued_for_delete.each do |path| | ||
| 208 | begin | ||
| 209 | logger.info("[paperclip] deleting #{path}") | ||
| 210 | if file = s3_bucket.key(path) | ||
| 211 | file.delete | ||
| 212 | end | ||
| 213 | rescue RightAws::AwsError | ||
| 214 | # Ignore this. | ||
| 215 | end | ||
| 216 | end | ||
| 217 | @queued_for_delete = [] | ||
| 218 | end | ||
| 219 | |||
| 220 | def find_credentials creds | ||
| 221 | case creds | ||
| 222 | when File | ||
| 223 | YAML.load_file(creds.path) | ||
| 224 | when String | ||
| 225 | YAML.load_file(creds) | ||
| 226 | when Hash | ||
| 227 | creds | ||
| 228 | else | ||
| 229 | raise ArgumentError, "Credentials are not a path, file, or hash." | ||
| 230 | end | ||
| 231 | end | ||
| 232 | private :find_credentials | ||
| 233 | |||
| 234 | end | ||
| 235 | end | ||
| 236 | end | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/thumbnail.rb b/vendor/plugins/paperclip/lib/paperclip/thumbnail.rb new file mode 100644 index 0000000..2178a9c --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/thumbnail.rb | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | module Paperclip | ||
| 2 | # Handles thumbnailing images that are uploaded. | ||
| 3 | class Thumbnail < Processor | ||
| 4 | |||
| 5 | attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options | ||
| 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+ is true (which it is, by default. If +convert_options+ is | ||
| 12 | # set, the options will be appended to the convert command upon image conversion | ||
| 13 | def initialize file, options = {}, attachment = nil | ||
| 14 | super | ||
| 15 | geometry = options[:geometry] | ||
| 16 | @file = file | ||
| 17 | @crop = geometry[-1,1] == '#' | ||
| 18 | @target_geometry = Geometry.parse geometry | ||
| 19 | @current_geometry = Geometry.from_file @file | ||
| 20 | @convert_options = options[:convert_options] | ||
| 21 | @whiny = options[:whiny].nil? ? true : options[:whiny] | ||
| 22 | @format = options[:format] | ||
| 23 | |||
| 24 | @current_format = File.extname(@file.path) | ||
| 25 | @basename = File.basename(@file.path, @current_format) | ||
| 26 | end | ||
| 27 | |||
| 28 | # Returns true if the +target_geometry+ is meant to crop. | ||
| 29 | def crop? | ||
| 30 | @crop | ||
| 31 | end | ||
| 32 | |||
| 33 | # Returns true if the image is meant to make use of additional convert options. | ||
| 34 | def convert_options? | ||
| 35 | not @convert_options.blank? | ||
| 36 | end | ||
| 37 | |||
| 38 | # Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile | ||
| 39 | # that contains the new image. | ||
| 40 | def make | ||
| 41 | src = @file | ||
| 42 | dst = Tempfile.new([@basename, @format].compact.join(".")) | ||
| 43 | dst.binmode | ||
| 44 | |||
| 45 | command = <<-end_command | ||
| 46 | "#{ File.expand_path(src.path) }[0]" | ||
| 47 | #{ transformation_command } | ||
| 48 | "#{ File.expand_path(dst.path) }" | ||
| 49 | end_command | ||
| 50 | |||
| 51 | begin | ||
| 52 | success = Paperclip.run("convert", command.gsub(/\s+/, " ")) | ||
| 53 | rescue PaperclipCommandLineError | ||
| 54 | raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if @whiny | ||
| 55 | end | ||
| 56 | |||
| 57 | dst | ||
| 58 | end | ||
| 59 | |||
| 60 | # Returns the command ImageMagick's +convert+ needs to transform the image | ||
| 61 | # into the thumbnail. | ||
| 62 | def transformation_command | ||
| 63 | scale, crop = @current_geometry.transformation_to(@target_geometry, crop?) | ||
| 64 | trans = "-resize \"#{scale}\"" | ||
| 65 | trans << " -crop \"#{crop}\" +repage" if crop | ||
| 66 | trans << " #{convert_options}" if convert_options? | ||
| 67 | trans | ||
| 68 | end | ||
| 69 | end | ||
| 70 | end | ||
diff --git a/vendor/plugins/paperclip/lib/paperclip/upfile.rb b/vendor/plugins/paperclip/lib/paperclip/upfile.rb new file mode 100644 index 0000000..f0c8a44 --- /dev/null +++ b/vendor/plugins/paperclip/lib/paperclip/upfile.rb | |||
| @@ -0,0 +1,48 @@ | |||
| 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").downcase | ||
| 10 | case type | ||
| 11 | when %r"jpe?g" then "image/jpeg" | ||
| 12 | when %r"tiff?" then "image/tiff" | ||
| 13 | when %r"png", "gif", "bmp" then "image/#{type}" | ||
| 14 | when "txt" then "text/plain" | ||
| 15 | when %r"html?" then "text/html" | ||
| 16 | when "csv", "xml", "css", "js" then "text/#{type}" | ||
| 17 | else "application/x-#{type}" | ||
| 18 | end | ||
| 19 | end | ||
| 20 | |||
| 21 | # Returns the file's normal name. | ||
| 22 | def original_filename | ||
| 23 | File.basename(self.path) | ||
| 24 | end | ||
| 25 | |||
| 26 | # Returns the size of the file. | ||
| 27 | def size | ||
| 28 | File.size(self) | ||
| 29 | end | ||
| 30 | end | ||
| 31 | end | ||
| 32 | |||
| 33 | if defined? StringIO | ||
| 34 | class StringIO | ||
| 35 | attr_accessor :original_filename, :content_type | ||
| 36 | def original_filename | ||
| 37 | @original_filename ||= "stringio.txt" | ||
| 38 | end | ||
| 39 | def content_type | ||
| 40 | @content_type ||= "text/plain" | ||
| 41 | end | ||
| 42 | end | ||
| 43 | end | ||
| 44 | |||
| 45 | class File #:nodoc: | ||
| 46 | include Paperclip::Upfile | ||
| 47 | end | ||
| 48 | |||
diff --git a/vendor/plugins/paperclip/paperclip.gemspec b/vendor/plugins/paperclip/paperclip.gemspec new file mode 100644 index 0000000..12ce4c2 --- /dev/null +++ b/vendor/plugins/paperclip/paperclip.gemspec | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | # -*- encoding: utf-8 -*- | ||
| 2 | |||
| 3 | Gem::Specification.new do |s| | ||
| 4 | s.name = %q{paperclip} | ||
| 5 | s.version = "2.2.8" | ||
| 6 | |||
| 7 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= | ||
| 8 | s.authors = ["Jon Yurek"] | ||
| 9 | s.date = %q{2009-04-02} | ||
| 10 | s.email = %q{jyurek@thoughtbot.com} | ||
| 11 | s.extra_rdoc_files = ["README.rdoc"] | ||
| 12 | s.files = ["README.rdoc", "LICENSE", "Rakefile", "init.rb", "generators/paperclip", "generators/paperclip/paperclip_generator.rb", "generators/paperclip/templates", "generators/paperclip/templates/paperclip_migration.rb.erb", "generators/paperclip/USAGE", "lib/paperclip", "lib/paperclip/attachment.rb", "lib/paperclip/callback_compatability.rb", "lib/paperclip/geometry.rb", "lib/paperclip/iostream.rb", "lib/paperclip/matchers", "lib/paperclip/matchers/have_attached_file_matcher.rb", "lib/paperclip/matchers/validate_attachment_content_type_matcher.rb", "lib/paperclip/matchers/validate_attachment_presence_matcher.rb", "lib/paperclip/matchers/validate_attachment_size_matcher.rb", "lib/paperclip/matchers.rb", "lib/paperclip/processor.rb", "lib/paperclip/storage.rb", "lib/paperclip/thumbnail.rb", "lib/paperclip/upfile.rb", "lib/paperclip.rb", "tasks/paperclip_tasks.rake", "test/attachment_test.rb", "test/database.yml", "test/debug.log", "test/fixtures", "test/fixtures/12k.png", "test/fixtures/50x50.png", "test/fixtures/5k.png", "test/fixtures/bad.png", "test/fixtures/s3.yml", "test/fixtures/text.txt", "test/fixtures/twopage.pdf", "test/geometry_test.rb", "test/helper.rb", "test/integration_test.rb", "test/iostream_test.rb", "test/matchers", "test/matchers/have_attached_file_matcher_test.rb", "test/matchers/validate_attachment_content_type_matcher_test.rb", "test/matchers/validate_attachment_presence_matcher_test.rb", "test/matchers/validate_attachment_size_matcher_test.rb", "test/paperclip_test.rb", "test/processor_test.rb", "test/s3.yml", "test/storage_test.rb", "test/thumbnail_test.rb", "test/tmp", "test/tmp/storage.txt", "shoulda_macros/paperclip.rb"] | ||
| 13 | s.has_rdoc = true | ||
| 14 | s.homepage = %q{http://www.thoughtbot.com/projects/paperclip} | ||
| 15 | s.rdoc_options = ["--line-numbers", "--inline-source"] | ||
| 16 | s.require_paths = ["lib"] | ||
| 17 | s.requirements = ["ImageMagick"] | ||
| 18 | s.rubyforge_project = %q{paperclip} | ||
| 19 | s.rubygems_version = %q{1.3.1} | ||
| 20 | s.summary = %q{File attachments as attributes for ActiveRecord} | ||
| 21 | |||
| 22 | if s.respond_to? :specification_version then | ||
| 23 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION | ||
| 24 | s.specification_version = 2 | ||
| 25 | |||
| 26 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then | ||
| 27 | s.add_runtime_dependency(%q<right_aws>, [">= 0"]) | ||
| 28 | s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"]) | ||
| 29 | s.add_development_dependency(%q<mocha>, [">= 0"]) | ||
| 30 | else | ||
| 31 | s.add_dependency(%q<right_aws>, [">= 0"]) | ||
| 32 | s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"]) | ||
| 33 | s.add_dependency(%q<mocha>, [">= 0"]) | ||
| 34 | end | ||
| 35 | else | ||
| 36 | s.add_dependency(%q<right_aws>, [">= 0"]) | ||
| 37 | s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"]) | ||
| 38 | s.add_dependency(%q<mocha>, [">= 0"]) | ||
| 39 | end | ||
| 40 | end | ||
diff --git a/vendor/plugins/paperclip/shoulda_macros/paperclip.rb b/vendor/plugins/paperclip/shoulda_macros/paperclip.rb new file mode 100644 index 0000000..d690357 --- /dev/null +++ b/vendor/plugins/paperclip/shoulda_macros/paperclip.rb | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | require 'paperclip/matchers' | ||
| 2 | |||
| 3 | module Paperclip | ||
| 4 | # =Paperclip Shoulda Macros | ||
| 5 | # | ||
| 6 | # These macros are intended for use with shoulda, and will be included into | ||
| 7 | # your tests automatically. All of the macros use the standard shoulda | ||
| 8 | # assumption that the name of the test is based on the name of the model | ||
| 9 | # you're testing (that is, UserTest is the test for the User model), and | ||
| 10 | # will load that class for testing purposes. | ||
| 11 | module Shoulda | ||
| 12 | include Matchers | ||
| 13 | # This will test whether you have defined your attachment correctly by | ||
| 14 | # checking for all the required fields exist after the definition of the | ||
| 15 | # attachment. | ||
| 16 | def should_have_attached_file name | ||
| 17 | klass = self.name.gsub(/Test$/, '').constantize | ||
| 18 | matcher = have_attached_file name | ||
| 19 | should matcher.description do | ||
| 20 | assert_accepts(matcher, klass) | ||
| 21 | end | ||
| 22 | end | ||
| 23 | |||
| 24 | # Tests for validations on the presence of the attachment. | ||
| 25 | def should_validate_attachment_presence name | ||
| 26 | klass = self.name.gsub(/Test$/, '').constantize | ||
| 27 | matcher = validate_attachment_presence name | ||
| 28 | should matcher.description do | ||
| 29 | assert_accepts(matcher, klass) | ||
| 30 | end | ||
| 31 | end | ||
| 32 | |||
| 33 | # Tests that you have content_type validations specified. There are two | ||
| 34 | # options, :valid and :invalid. Both accept an array of strings. The | ||
| 35 | # strings should be a list of content types which will pass and fail | ||
| 36 | # validation, respectively. | ||
| 37 | def should_validate_attachment_content_type name, options = {} | ||
| 38 | klass = self.name.gsub(/Test$/, '').constantize | ||
| 39 | valid = [options[:valid]].flatten | ||
| 40 | invalid = [options[:invalid]].flatten | ||
| 41 | matcher = validate_attachment_content_type(name).allowing(valid).rejecting(invalid) | ||
| 42 | should matcher.description do | ||
| 43 | assert_accepts(matcher, klass) | ||
| 44 | end | ||
| 45 | end | ||
| 46 | |||
| 47 | # Tests to ensure that you have file size validations turned on. You | ||
| 48 | # can pass the same options to this that you can to | ||
| 49 | # validate_attachment_file_size - :less_than, :greater_than, and :in. | ||
| 50 | # :less_than checks that a file is less than a certain size, :greater_than | ||
| 51 | # checks that a file is more than a certain size, and :in takes a Range or | ||
| 52 | # Array which specifies the lower and upper limits of the file size. | ||
| 53 | def should_validate_attachment_size name, options = {} | ||
| 54 | klass = self.name.gsub(/Test$/, '').constantize | ||
| 55 | min = options[:greater_than] || (options[:in] && options[:in].first) || 0 | ||
| 56 | max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0) | ||
| 57 | range = (min..max) | ||
| 58 | matcher = validate_attachment_size(name).in(range) | ||
| 59 | should matcher.description do | ||
| 60 | assert_accepts(matcher, klass) | ||
| 61 | end | ||
| 62 | end | ||
| 63 | end | ||
| 64 | end | ||
| 65 | |||
| 66 | class Test::Unit::TestCase #:nodoc: | ||
| 67 | extend Paperclip::Shoulda | ||
| 68 | end | ||
diff --git a/vendor/plugins/paperclip/tasks/paperclip_tasks.rake b/vendor/plugins/paperclip/tasks/paperclip_tasks.rake new file mode 100644 index 0000000..23e4c11 --- /dev/null +++ b/vendor/plugins/paperclip/tasks/paperclip_tasks.rake | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | def obtain_class | ||
| 2 | class_name = ENV['CLASS'] || ENV['class'] | ||
| 3 | raise "Must specify CLASS" unless class_name | ||
| 4 | @klass = Object.const_get(class_name) | ||
| 5 | end | ||
| 6 | |||
| 7 | def obtain_attachments | ||
| 8 | name = ENV['ATTACHMENT'] || ENV['attachment'] | ||
| 9 | raise "Class #{@klass.name} has no attachments specified" unless @klass.respond_to?(:attachment_definitions) | ||
| 10 | if !name.blank? && @klass.attachment_definitions.keys.include?(name) | ||
| 11 | [ name ] | ||
| 12 | else | ||
| 13 | @klass.attachment_definitions.keys | ||
| 14 | end | ||
| 15 | end | ||
| 16 | |||
| 17 | def for_all_attachments | ||
| 18 | klass = obtain_class | ||
| 19 | names = obtain_attachments | ||
| 20 | ids = klass.connection.select_values(klass.send(:construct_finder_sql, :select => 'id')) | ||
| 21 | |||
| 22 | ids.each do |id| | ||
| 23 | instance = klass.find(id) | ||
| 24 | names.each do |name| | ||
| 25 | result = if instance.send("#{ name }?") | ||
| 26 | yield(instance, name) | ||
| 27 | else | ||
| 28 | true | ||
| 29 | end | ||
| 30 | print result ? "." : "x"; $stdout.flush | ||
| 31 | end | ||
| 32 | end | ||
| 33 | puts " Done." | ||
| 34 | end | ||
| 35 | |||
| 36 | namespace :paperclip do | ||
| 37 | desc "Refreshes both metadata and thumbnails." | ||
| 38 | task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"] | ||
| 39 | |||
| 40 | namespace :refresh do | ||
| 41 | desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT)." | ||
| 42 | task :thumbnails => :environment do | ||
| 43 | errors = [] | ||
| 44 | for_all_attachments do |instance, name| | ||
| 45 | result = instance.send(name).reprocess! | ||
| 46 | errors << [instance.id, instance.errors] unless instance.errors.blank? | ||
| 47 | result | ||
| 48 | end | ||
| 49 | errors.each{|e| puts "#{e.first}: #{e.last.full_messages.inspect}" } | ||
| 50 | end | ||
| 51 | |||
| 52 | desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)." | ||
| 53 | task :metadata => :environment do | ||
| 54 | for_all_attachments do |instance, name| | ||
| 55 | if file = instance.send(name).to_file | ||
| 56 | instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip) | ||
| 57 | instance.send("#{name}_content_type=", file.content_type.strip) | ||
| 58 | instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size") | ||
| 59 | instance.save(false) | ||
| 60 | else | ||
| 61 | true | ||
| 62 | end | ||
| 63 | end | ||
| 64 | end | ||
| 65 | end | ||
| 66 | |||
| 67 | desc "Cleans out invalid attachments. Useful after you've added new validations." | ||
| 68 | task :clean => :environment do | ||
| 69 | for_all_attachments do |instance, name| | ||
| 70 | instance.send(name).send(:validate) | ||
| 71 | if instance.send(name).valid? | ||
| 72 | true | ||
| 73 | else | ||
| 74 | instance.send("#{name}=", nil) | ||
| 75 | instance.save | ||
| 76 | end | ||
| 77 | end | ||
| 78 | end | ||
| 79 | end | ||
diff --git a/vendor/plugins/paperclip/test/.gitignore b/vendor/plugins/paperclip/test/.gitignore new file mode 100644 index 0000000..b14c548 --- /dev/null +++ b/vendor/plugins/paperclip/test/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| debug.log | |||
diff --git a/vendor/plugins/paperclip/test/attachment_test.rb b/vendor/plugins/paperclip/test/attachment_test.rb new file mode 100644 index 0000000..61ab2a2 --- /dev/null +++ b/vendor/plugins/paperclip/test/attachment_test.rb | |||
| @@ -0,0 +1,742 @@ | |||
| 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 "Attachment default_options" do | ||
| 9 | setup do | ||
| 10 | rebuild_model | ||
| 11 | @old_default_options = Paperclip::Attachment.default_options.dup | ||
| 12 | @new_default_options = @old_default_options.merge({ | ||
| 13 | :path => "argle/bargle", | ||
| 14 | :url => "fooferon", | ||
| 15 | :default_url => "not here.png" | ||
| 16 | }) | ||
| 17 | end | ||
| 18 | |||
| 19 | teardown do | ||
| 20 | Paperclip::Attachment.default_options.merge! @old_default_options | ||
| 21 | end | ||
| 22 | |||
| 23 | should "be overrideable" do | ||
| 24 | Paperclip::Attachment.default_options.merge!(@new_default_options) | ||
| 25 | @new_default_options.keys.each do |key| | ||
| 26 | assert_equal @new_default_options[key], | ||
| 27 | Paperclip::Attachment.default_options[key] | ||
| 28 | end | ||
| 29 | end | ||
| 30 | |||
| 31 | context "without an Attachment" do | ||
| 32 | setup do | ||
| 33 | @dummy = Dummy.new | ||
| 34 | end | ||
| 35 | |||
| 36 | should "return false when asked exists?" do | ||
| 37 | assert !@dummy.avatar.exists? | ||
| 38 | end | ||
| 39 | end | ||
| 40 | |||
| 41 | context "on an Attachment" do | ||
| 42 | setup do | ||
| 43 | @dummy = Dummy.new | ||
| 44 | @attachment = @dummy.avatar | ||
| 45 | end | ||
| 46 | |||
| 47 | Paperclip::Attachment.default_options.keys.each do |key| | ||
| 48 | should "be the default_options for #{key}" do | ||
| 49 | assert_equal @old_default_options[key], | ||
| 50 | @attachment.instance_variable_get("@#{key}"), | ||
| 51 | key | ||
| 52 | end | ||
| 53 | end | ||
| 54 | |||
| 55 | context "when redefined" do | ||
| 56 | setup do | ||
| 57 | Paperclip::Attachment.default_options.merge!(@new_default_options) | ||
| 58 | @dummy = Dummy.new | ||
| 59 | @attachment = @dummy.avatar | ||
| 60 | end | ||
| 61 | |||
| 62 | Paperclip::Attachment.default_options.keys.each do |key| | ||
| 63 | should "be the new default_options for #{key}" do | ||
| 64 | assert_equal @new_default_options[key], | ||
| 65 | @attachment.instance_variable_get("@#{key}"), | ||
| 66 | key | ||
| 67 | end | ||
| 68 | end | ||
| 69 | end | ||
| 70 | end | ||
| 71 | end | ||
| 72 | |||
| 73 | context "An attachment with similarly named interpolations" do | ||
| 74 | setup do | ||
| 75 | rebuild_model :path => ":id.omg/:id-bbq/:idwhat/:id_partition.wtf" | ||
| 76 | @dummy = Dummy.new | ||
| 77 | @dummy.stubs(:id).returns(1024) | ||
| 78 | @file = File.new(File.join(File.dirname(__FILE__), | ||
| 79 | "fixtures", | ||
| 80 | "5k.png"), 'rb') | ||
| 81 | @dummy.avatar = @file | ||
| 82 | end | ||
| 83 | |||
| 84 | teardown { @file.close } | ||
| 85 | |||
| 86 | should "make sure that they are interpolated correctly" do | ||
| 87 | assert_equal "1024.omg/1024-bbq/1024what/000/001/024.wtf", @dummy.avatar.path | ||
| 88 | end | ||
| 89 | end | ||
| 90 | |||
| 91 | context "An attachment with a :rails_env interpolation" do | ||
| 92 | setup do | ||
| 93 | @rails_env = "blah" | ||
| 94 | @id = 1024 | ||
| 95 | rebuild_model :path => ":rails_env/:id.png" | ||
| 96 | @dummy = Dummy.new | ||
| 97 | @dummy.stubs(:id).returns(@id) | ||
| 98 | @file = StringIO.new(".") | ||
| 99 | @dummy.avatar = @file | ||
| 100 | end | ||
| 101 | |||
| 102 | should "return the proper path" do | ||
| 103 | temporary_rails_env(@rails_env) { | ||
| 104 | assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path | ||
| 105 | } | ||
| 106 | end | ||
| 107 | end | ||
| 108 | |||
| 109 | context "An attachment with :convert_options" do | ||
| 110 | setup do | ||
| 111 | rebuild_model :styles => { | ||
| 112 | :thumb => "100x100", | ||
| 113 | :large => "400x400" | ||
| 114 | }, | ||
| 115 | :convert_options => { | ||
| 116 | :all => "-do_stuff", | ||
| 117 | :thumb => "-thumbnailize" | ||
| 118 | } | ||
| 119 | @dummy = Dummy.new | ||
| 120 | @dummy.avatar | ||
| 121 | end | ||
| 122 | |||
| 123 | should "report the correct options when sent #extra_options_for(:thumb)" do | ||
| 124 | assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect | ||
| 125 | end | ||
| 126 | |||
| 127 | should "report the correct options when sent #extra_options_for(:large)" do | ||
| 128 | assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large) | ||
| 129 | end | ||
| 130 | |||
| 131 | before_should "call extra_options_for(:thumb/:large)" do | ||
| 132 | Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb) | ||
| 133 | Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large) | ||
| 134 | end | ||
| 135 | end | ||
| 136 | |||
| 137 | context "An attachment with :convert_options that is a proc" do | ||
| 138 | setup do | ||
| 139 | rebuild_model :styles => { | ||
| 140 | :thumb => "100x100", | ||
| 141 | :large => "400x400" | ||
| 142 | }, | ||
| 143 | :convert_options => { | ||
| 144 | :all => lambda{|i| i.all }, | ||
| 145 | :thumb => lambda{|i| i.thumb } | ||
| 146 | } | ||
| 147 | Dummy.class_eval do | ||
| 148 | def all; "-all"; end | ||
| 149 | def thumb; "-thumb"; end | ||
| 150 | end | ||
| 151 | @dummy = Dummy.new | ||
| 152 | @dummy.avatar | ||
| 153 | end | ||
| 154 | |||
| 155 | should "report the correct options when sent #extra_options_for(:thumb)" do | ||
| 156 | assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect | ||
| 157 | end | ||
| 158 | |||
| 159 | should "report the correct options when sent #extra_options_for(:large)" do | ||
| 160 | assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large) | ||
| 161 | end | ||
| 162 | |||
| 163 | before_should "call extra_options_for(:thumb/:large)" do | ||
| 164 | Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb) | ||
| 165 | Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large) | ||
| 166 | end | ||
| 167 | end | ||
| 168 | |||
| 169 | context "An attachment with :path that is a proc" do | ||
| 170 | setup do | ||
| 171 | rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" } | ||
| 172 | |||
| 173 | @file = File.new(File.join(File.dirname(__FILE__), | ||
| 174 | "fixtures", | ||
| 175 | "5k.png"), 'rb') | ||
| 176 | @dummyA = Dummy.new(:other => 'a') | ||
| 177 | @dummyA.avatar = @file | ||
| 178 | @dummyB = Dummy.new(:other => 'b') | ||
| 179 | @dummyB.avatar = @file | ||
| 180 | end | ||
| 181 | |||
| 182 | teardown { @file.close } | ||
| 183 | |||
| 184 | should "return correct path" do | ||
| 185 | assert_equal "path/a.png", @dummyA.avatar.path | ||
| 186 | assert_equal "path/b.png", @dummyB.avatar.path | ||
| 187 | end | ||
| 188 | end | ||
| 189 | |||
| 190 | context "An attachment with :styles that is a proc" do | ||
| 191 | setup do | ||
| 192 | rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} } | ||
| 193 | |||
| 194 | @attachment = Dummy.new.avatar | ||
| 195 | end | ||
| 196 | |||
| 197 | should "have the correct geometry" do | ||
| 198 | assert_equal "50x50#", @attachment.styles[:thumb][:geometry] | ||
| 199 | end | ||
| 200 | end | ||
| 201 | |||
| 202 | context "An attachment with :url that is a proc" do | ||
| 203 | setup do | ||
| 204 | rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" } | ||
| 205 | |||
| 206 | @file = File.new(File.join(File.dirname(__FILE__), | ||
| 207 | "fixtures", | ||
| 208 | "5k.png"), 'rb') | ||
| 209 | @dummyA = Dummy.new(:other => 'a') | ||
| 210 | @dummyA.avatar = @file | ||
| 211 | @dummyB = Dummy.new(:other => 'b') | ||
| 212 | @dummyB.avatar = @file | ||
| 213 | end | ||
| 214 | |||
| 215 | teardown { @file.close } | ||
| 216 | |||
| 217 | should "return correct url" do | ||
| 218 | assert_equal "path/a.png", @dummyA.avatar.url(:original, false) | ||
| 219 | assert_equal "path/b.png", @dummyB.avatar.url(:original, false) | ||
| 220 | end | ||
| 221 | end | ||
| 222 | |||
| 223 | geometry_specs = [ | ||
| 224 | [ lambda{|z| "50x50#" }, :png ], | ||
| 225 | lambda{|z| "50x50#" }, | ||
| 226 | { :geometry => lambda{|z| "50x50#" } } | ||
| 227 | ] | ||
| 228 | geometry_specs.each do |geometry_spec| | ||
| 229 | context "An attachment geometry like #{geometry_spec}" do | ||
| 230 | setup do | ||
| 231 | rebuild_model :styles => { :normal => geometry_spec } | ||
| 232 | @attachment = Dummy.new.avatar | ||
| 233 | end | ||
| 234 | |||
| 235 | should "not run the procs immediately" do | ||
| 236 | assert_kind_of Proc, @attachment.styles[:normal][:geometry] | ||
| 237 | end | ||
| 238 | |||
| 239 | context "when assigned" do | ||
| 240 | setup do | ||
| 241 | @file = StringIO.new(".") | ||
| 242 | @attachment.assign(@file) | ||
| 243 | end | ||
| 244 | |||
| 245 | should "have the correct geometry" do | ||
| 246 | assert_equal "50x50#", @attachment.styles[:normal][:geometry] | ||
| 247 | end | ||
| 248 | end | ||
| 249 | end | ||
| 250 | end | ||
| 251 | |||
| 252 | context "An attachment with both 'normal' and hash-style styles" do | ||
| 253 | setup do | ||
| 254 | rebuild_model :styles => { | ||
| 255 | :normal => ["50x50#", :png], | ||
| 256 | :hash => { :geometry => "50x50#", :format => :png } | ||
| 257 | } | ||
| 258 | @dummy = Dummy.new | ||
| 259 | @attachment = @dummy.avatar | ||
| 260 | end | ||
| 261 | |||
| 262 | [:processors, :whiny, :convert_options, :geometry, :format].each do |field| | ||
| 263 | should "have the same #{field} field" do | ||
| 264 | assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field] | ||
| 265 | end | ||
| 266 | end | ||
| 267 | end | ||
| 268 | |||
| 269 | context "An attachment with :processors that is a proc" do | ||
| 270 | setup do | ||
| 271 | rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] } | ||
| 272 | @attachment = Dummy.new.avatar | ||
| 273 | end | ||
| 274 | |||
| 275 | should "not run the proc immediately" do | ||
| 276 | assert_kind_of Proc, @attachment.styles[:normal][:processors] | ||
| 277 | end | ||
| 278 | |||
| 279 | context "when assigned" do | ||
| 280 | setup do | ||
| 281 | @attachment.assign(StringIO.new(".")) | ||
| 282 | end | ||
| 283 | |||
| 284 | should "have the correct processors" do | ||
| 285 | assert_equal [ :test ], @attachment.styles[:normal][:processors] | ||
| 286 | end | ||
| 287 | end | ||
| 288 | end | ||
| 289 | |||
| 290 | context "An attachment with erroring processor" do | ||
| 291 | setup do | ||
| 292 | rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true | ||
| 293 | @dummy = Dummy.new | ||
| 294 | Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.") | ||
| 295 | @file = StringIO.new("...") | ||
| 296 | @file.stubs(:to_tempfile).returns(@file) | ||
| 297 | @dummy.avatar = @file | ||
| 298 | end | ||
| 299 | |||
| 300 | should "correctly forward processing error message to the instance" do | ||
| 301 | @dummy.valid? | ||
| 302 | assert_contains @dummy.errors.full_messages, "Avatar cannot be processed." | ||
| 303 | end | ||
| 304 | end | ||
| 305 | |||
| 306 | context "An attachment with multiple processors" do | ||
| 307 | setup do | ||
| 308 | class Paperclip::Test < Paperclip::Processor; end | ||
| 309 | @style_params = { :once => {:one => 1, :two => 2} } | ||
| 310 | rebuild_model :processors => [:thumbnail, :test], :styles => @style_params | ||
| 311 | @dummy = Dummy.new | ||
| 312 | @file = StringIO.new("...") | ||
| 313 | @file.stubs(:to_tempfile).returns(@file) | ||
| 314 | Paperclip::Test.stubs(:make).returns(@file) | ||
| 315 | Paperclip::Thumbnail.stubs(:make).returns(@file) | ||
| 316 | end | ||
| 317 | |||
| 318 | context "when assigned" do | ||
| 319 | setup { @dummy.avatar = @file } | ||
| 320 | |||
| 321 | before_should "call #make on all specified processors" do | ||
| 322 | expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => nil, :convert_options => ""}) | ||
| 323 | Paperclip::Thumbnail.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file) | ||
| 324 | Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file) | ||
| 325 | end | ||
| 326 | |||
| 327 | before_should "call #make with attachment passed as third argument" do | ||
| 328 | expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => nil, :convert_options => ""}) | ||
| 329 | Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file) | ||
| 330 | end | ||
| 331 | end | ||
| 332 | end | ||
| 333 | |||
| 334 | context "An attachment with no processors defined" do | ||
| 335 | setup do | ||
| 336 | rebuild_model :processors => [], :styles => {:something => 1} | ||
| 337 | @dummy = Dummy.new | ||
| 338 | @file = StringIO.new("...") | ||
| 339 | end | ||
| 340 | should "raise when assigned to" do | ||
| 341 | assert_raises(RuntimeError){ @dummy.avatar = @file } | ||
| 342 | end | ||
| 343 | end | ||
| 344 | |||
| 345 | context "Assigning an attachment with post_process hooks" do | ||
| 346 | setup do | ||
| 347 | rebuild_model :styles => { :something => "100x100#" } | ||
| 348 | Dummy.class_eval do | ||
| 349 | before_avatar_post_process :do_before_avatar | ||
| 350 | after_avatar_post_process :do_after_avatar | ||
| 351 | before_post_process :do_before_all | ||
| 352 | after_post_process :do_after_all | ||
| 353 | def do_before_avatar; end | ||
| 354 | def do_after_avatar; end | ||
| 355 | def do_before_all; end | ||
| 356 | def do_after_all; end | ||
| 357 | end | ||
| 358 | @file = StringIO.new(".") | ||
| 359 | @file.stubs(:to_tempfile).returns(@file) | ||
| 360 | @dummy = Dummy.new | ||
| 361 | Paperclip::Thumbnail.stubs(:make).returns(@file) | ||
| 362 | @attachment = @dummy.avatar | ||
| 363 | end | ||
| 364 | |||
| 365 | should "call the defined callbacks when assigned" do | ||
| 366 | @dummy.expects(:do_before_avatar).with() | ||
| 367 | @dummy.expects(:do_after_avatar).with() | ||
| 368 | @dummy.expects(:do_before_all).with() | ||
| 369 | @dummy.expects(:do_after_all).with() | ||
| 370 | Paperclip::Thumbnail.expects(:make).returns(@file) | ||
| 371 | @dummy.avatar = @file | ||
| 372 | end | ||
| 373 | |||
| 374 | should "not cancel the processing if a before_post_process returns nil" do | ||
| 375 | @dummy.expects(:do_before_avatar).with().returns(nil) | ||
| 376 | @dummy.expects(:do_after_avatar).with() | ||
| 377 | @dummy.expects(:do_before_all).with().returns(nil) | ||
| 378 | @dummy.expects(:do_after_all).with() | ||
| 379 | Paperclip::Thumbnail.expects(:make).returns(@file) | ||
| 380 | @dummy.avatar = @file | ||
| 381 | end | ||
| 382 | |||
| 383 | should "cancel the processing if a before_post_process returns false" do | ||
| 384 | @dummy.expects(:do_before_avatar).never | ||
| 385 | @dummy.expects(:do_after_avatar).never | ||
| 386 | @dummy.expects(:do_before_all).with().returns(false) | ||
| 387 | @dummy.expects(:do_after_all).never | ||
| 388 | Paperclip::Thumbnail.expects(:make).never | ||
| 389 | @dummy.avatar = @file | ||
| 390 | end | ||
| 391 | |||
| 392 | should "cancel the processing if a before_avatar_post_process returns false" do | ||
| 393 | @dummy.expects(:do_before_avatar).with().returns(false) | ||
| 394 | @dummy.expects(:do_after_avatar).never | ||
| 395 | @dummy.expects(:do_before_all).with().returns(true) | ||
| 396 | @dummy.expects(:do_after_all).never | ||
| 397 | Paperclip::Thumbnail.expects(:make).never | ||
| 398 | @dummy.avatar = @file | ||
| 399 | end | ||
| 400 | end | ||
| 401 | |||
| 402 | context "Assigning an attachment" do | ||
| 403 | setup do | ||
| 404 | rebuild_model :styles => { :something => "100x100#" } | ||
| 405 | @file = StringIO.new(".") | ||
| 406 | @file.expects(:original_filename).returns("5k.png\n\n") | ||
| 407 | @file.expects(:content_type).returns("image/png\n\n") | ||
| 408 | @file.stubs(:to_tempfile).returns(@file) | ||
| 409 | @dummy = Dummy.new | ||
| 410 | Paperclip::Thumbnail.expects(:make).returns(@file) | ||
| 411 | @dummy.expects(:run_callbacks).with(:before_avatar_post_process, {:original => @file}) | ||
| 412 | @dummy.expects(:run_callbacks).with(:before_post_process, {:original => @file}) | ||
| 413 | @dummy.expects(:run_callbacks).with(:after_avatar_post_process, {:original => @file, :something => @file}) | ||
| 414 | @dummy.expects(:run_callbacks).with(:after_post_process, {:original => @file, :something => @file}) | ||
| 415 | @attachment = @dummy.avatar | ||
| 416 | @dummy.avatar = @file | ||
| 417 | end | ||
| 418 | |||
| 419 | should "strip whitespace from original_filename field" do | ||
| 420 | assert_equal "5k.png", @dummy.avatar.original_filename | ||
| 421 | end | ||
| 422 | |||
| 423 | should "strip whitespace from content_type field" do | ||
| 424 | assert_equal "image/png", @dummy.avatar.instance.avatar_content_type | ||
| 425 | end | ||
| 426 | end | ||
| 427 | |||
| 428 | context "Attachment with strange letters" do | ||
| 429 | setup do | ||
| 430 | rebuild_model | ||
| 431 | |||
| 432 | @not_file = mock | ||
| 433 | @tempfile = mock | ||
| 434 | @not_file.stubs(:nil?).returns(false) | ||
| 435 | @not_file.expects(:size).returns(10) | ||
| 436 | @tempfile.expects(:size).returns(10) | ||
| 437 | @not_file.expects(:to_tempfile).returns(@tempfile) | ||
| 438 | @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n") | ||
| 439 | @not_file.expects(:content_type).returns("image/png\r\n") | ||
| 440 | |||
| 441 | @dummy = Dummy.new | ||
| 442 | @attachment = @dummy.avatar | ||
| 443 | @attachment.expects(:valid_assignment?).with(@not_file).returns(true) | ||
| 444 | @attachment.expects(:queue_existing_for_delete) | ||
| 445 | @attachment.expects(:post_process) | ||
| 446 | @attachment.expects(:valid?).returns(true) | ||
| 447 | @attachment.expects(:validate) | ||
| 448 | @dummy.avatar = @not_file | ||
| 449 | end | ||
| 450 | |||
| 451 | should "remove strange letters and replace with underscore (_)" do | ||
| 452 | assert_equal "sheep_say_b_.png", @dummy.avatar.original_filename | ||
| 453 | end | ||
| 454 | |||
| 455 | end | ||
| 456 | |||
| 457 | context "An attachment" do | ||
| 458 | setup do | ||
| 459 | Paperclip::Attachment.default_options.merge!({ | ||
| 460 | :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension" | ||
| 461 | }) | ||
| 462 | FileUtils.rm_rf("tmp") | ||
| 463 | rebuild_model | ||
| 464 | @instance = Dummy.new | ||
| 465 | @attachment = Paperclip::Attachment.new(:avatar, @instance) | ||
| 466 | @file = File.new(File.join(File.dirname(__FILE__), | ||
| 467 | "fixtures", | ||
| 468 | "5k.png"), 'rb') | ||
| 469 | end | ||
| 470 | |||
| 471 | teardown { @file.close } | ||
| 472 | |||
| 473 | should "raise if there are not the correct columns when you try to assign" do | ||
| 474 | @other_attachment = Paperclip::Attachment.new(:not_here, @instance) | ||
| 475 | assert_raises(Paperclip::PaperclipError) do | ||
| 476 | @other_attachment.assign(@file) | ||
| 477 | end | ||
| 478 | end | ||
| 479 | |||
| 480 | should "return its default_url when no file assigned" do | ||
| 481 | assert @attachment.to_file.nil? | ||
| 482 | assert_equal "/avatars/original/missing.png", @attachment.url | ||
| 483 | assert_equal "/avatars/blah/missing.png", @attachment.url(:blah) | ||
| 484 | end | ||
| 485 | |||
| 486 | should "return nil as path when no file assigned" do | ||
| 487 | assert @attachment.to_file.nil? | ||
| 488 | assert_equal nil, @attachment.path | ||
| 489 | assert_equal nil, @attachment.path(:blah) | ||
| 490 | end | ||
| 491 | |||
| 492 | context "with a file assigned in the database" do | ||
| 493 | setup do | ||
| 494 | @attachment.stubs(:instance_read).with(:file_name).returns("5k.png") | ||
| 495 | @attachment.stubs(:instance_read).with(:content_type).returns("image/png") | ||
| 496 | @attachment.stubs(:instance_read).with(:file_size).returns(12345) | ||
| 497 | now = Time.now | ||
| 498 | Time.stubs(:now).returns(now) | ||
| 499 | @attachment.stubs(:instance_read).with(:updated_at).returns(Time.now) | ||
| 500 | end | ||
| 501 | |||
| 502 | should "return a correct url even if the file does not exist" do | ||
| 503 | assert_nil @attachment.to_file | ||
| 504 | assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah) | ||
| 505 | end | ||
| 506 | |||
| 507 | should "make sure the updated_at mtime is in the url if it is defined" do | ||
| 508 | assert_match %r{#{Time.now.to_i}$}, @attachment.url(:blah) | ||
| 509 | end | ||
| 510 | |||
| 511 | should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do | ||
| 512 | assert_no_match %r{#{Time.now.to_i}$}, @attachment.url(:blah, false) | ||
| 513 | end | ||
| 514 | |||
| 515 | context "with the updated_at field removed" do | ||
| 516 | setup do | ||
| 517 | @attachment.stubs(:instance_read).with(:updated_at).returns(nil) | ||
| 518 | end | ||
| 519 | |||
| 520 | should "only return the url without the updated_at when sent #url" do | ||
| 521 | assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah) | ||
| 522 | end | ||
| 523 | end | ||
| 524 | |||
| 525 | should "return the proper path when filename has a single .'s" do | ||
| 526 | assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png", @attachment.path | ||
| 527 | end | ||
| 528 | |||
| 529 | should "return the proper path when filename has multiple .'s" do | ||
| 530 | @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png") | ||
| 531 | assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png", @attachment.path | ||
| 532 | end | ||
| 533 | |||
| 534 | context "when expecting three styles" do | ||
| 535 | setup do | ||
| 536 | styles = {:styles => { :large => ["400x400", :png], | ||
| 537 | :medium => ["100x100", :gif], | ||
| 538 | :small => ["32x32#", :jpg]}} | ||
| 539 | @attachment = Paperclip::Attachment.new(:avatar, | ||
| 540 | @instance, | ||
| 541 | styles) | ||
| 542 | end | ||
| 543 | |||
| 544 | context "and assigned a file" do | ||
| 545 | setup do | ||
| 546 | now = Time.now | ||
| 547 | Time.stubs(:now).returns(now) | ||
| 548 | @attachment.assign(@file) | ||
| 549 | end | ||
| 550 | |||
| 551 | should "be dirty" do | ||
| 552 | assert @attachment.dirty? | ||
| 553 | end | ||
| 554 | |||
| 555 | context "and saved" do | ||
| 556 | setup do | ||
| 557 | @attachment.save | ||
| 558 | end | ||
| 559 | |||
| 560 | should "return the real url" do | ||
| 561 | file = @attachment.to_file | ||
| 562 | assert file | ||
| 563 | assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url | ||
| 564 | assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small) | ||
| 565 | file.close | ||
| 566 | end | ||
| 567 | |||
| 568 | should "commit the files to disk" do | ||
| 569 | [:large, :medium, :small].each do |style| | ||
| 570 | io = @attachment.to_io(style) | ||
| 571 | assert File.exists?(io) | ||
| 572 | assert ! io.is_a?(::Tempfile) | ||
| 573 | io.close | ||
| 574 | end | ||
| 575 | end | ||
| 576 | |||
| 577 | should "save the files as the right formats and sizes" do | ||
| 578 | [[:large, 400, 61, "PNG"], | ||
| 579 | [:medium, 100, 15, "GIF"], | ||
| 580 | [:small, 32, 32, "JPEG"]].each do |style| | ||
| 581 | cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"] | ||
| 582 | out = `#{cmd}` | ||
| 583 | width, height, size, format = out.split(" ") | ||
| 584 | assert_equal style[1].to_s, width.to_s | ||
| 585 | assert_equal style[2].to_s, height.to_s | ||
| 586 | assert_equal style[3].to_s, format.to_s | ||
| 587 | end | ||
| 588 | end | ||
| 589 | |||
| 590 | should "still have its #file attribute not be nil" do | ||
| 591 | assert ! (file = @attachment.to_file).nil? | ||
| 592 | file.close | ||
| 593 | end | ||
| 594 | |||
| 595 | context "and trying to delete" do | ||
| 596 | setup do | ||
| 597 | @existing_names = @attachment.styles.keys.collect do |style| | ||
| 598 | @attachment.path(style) | ||
| 599 | end | ||
| 600 | end | ||
| 601 | |||
| 602 | should "delete the files after assigning nil" do | ||
| 603 | @attachment.expects(:instance_write).with(:file_name, nil) | ||
| 604 | @attachment.expects(:instance_write).with(:content_type, nil) | ||
| 605 | @attachment.expects(:instance_write).with(:file_size, nil) | ||
| 606 | @attachment.expects(:instance_write).with(:updated_at, nil) | ||
| 607 | @attachment.assign nil | ||
| 608 | @attachment.save | ||
| 609 | @existing_names.each{|f| assert ! File.exists?(f) } | ||
| 610 | end | ||
| 611 | |||
| 612 | should "delete the files when you call #clear and #save" do | ||
| 613 | @attachment.expects(:instance_write).with(:file_name, nil) | ||
| 614 | @attachment.expects(:instance_write).with(:content_type, nil) | ||
| 615 | @attachment.expects(:instance_write).with(:file_size, nil) | ||
| 616 | @attachment.expects(:instance_write).with(:updated_at, nil) | ||
| 617 | @attachment.clear | ||
| 618 | @attachment.save | ||
| 619 | @existing_names.each{|f| assert ! File.exists?(f) } | ||
| 620 | end | ||
| 621 | |||
| 622 | should "delete the files when you call #delete" do | ||
| 623 | @attachment.expects(:instance_write).with(:file_name, nil) | ||
| 624 | @attachment.expects(:instance_write).with(:content_type, nil) | ||
| 625 | @attachment.expects(:instance_write).with(:file_size, nil) | ||
| 626 | @attachment.expects(:instance_write).with(:updated_at, nil) | ||
| 627 | @attachment.destroy | ||
| 628 | @existing_names.each{|f| assert ! File.exists?(f) } | ||
| 629 | end | ||
| 630 | end | ||
| 631 | end | ||
| 632 | end | ||
| 633 | end | ||
| 634 | |||
| 635 | end | ||
| 636 | |||
| 637 | context "when trying a nonexistant storage type" do | ||
| 638 | setup do | ||
| 639 | rebuild_model :storage => :not_here | ||
| 640 | end | ||
| 641 | |||
| 642 | should "not be able to find the module" do | ||
| 643 | assert_raise(NameError){ Dummy.new.avatar } | ||
| 644 | end | ||
| 645 | end | ||
| 646 | end | ||
| 647 | |||
| 648 | context "An attachment with only a avatar_file_name column" do | ||
| 649 | setup do | ||
| 650 | ActiveRecord::Base.connection.create_table :dummies, :force => true do |table| | ||
| 651 | table.column :avatar_file_name, :string | ||
| 652 | end | ||
| 653 | rebuild_class | ||
| 654 | @dummy = Dummy.new | ||
| 655 | @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb') | ||
| 656 | end | ||
| 657 | |||
| 658 | teardown { @file.close } | ||
| 659 | |||
| 660 | should "not error when assigned an attachment" do | ||
| 661 | assert_nothing_raised { @dummy.avatar = @file } | ||
| 662 | end | ||
| 663 | |||
| 664 | should "return the time when sent #avatar_updated_at" do | ||
| 665 | now = Time.now | ||
| 666 | Time.stubs(:now).returns(now) | ||
| 667 | @dummy.avatar = @file | ||
| 668 | assert now, @dummy.avatar.updated_at | ||
| 669 | end | ||
| 670 | |||
| 671 | should "return nil when reloaded and sent #avatar_updated_at" do | ||
| 672 | @dummy.save | ||
| 673 | @dummy.reload | ||
| 674 | assert_nil @dummy.avatar.updated_at | ||
| 675 | end | ||
| 676 | |||
| 677 | should "return the right value when sent #avatar_file_size" do | ||
| 678 | @dummy.avatar = @file | ||
| 679 | assert_equal @file.size, @dummy.avatar.size | ||
| 680 | end | ||
| 681 | |||
| 682 | context "and avatar_updated_at column" do | ||
| 683 | setup do | ||
| 684 | ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp | ||
| 685 | rebuild_class | ||
| 686 | @dummy = Dummy.new | ||
| 687 | end | ||
| 688 | |||
| 689 | should "not error when assigned an attachment" do | ||
| 690 | assert_nothing_raised { @dummy.avatar = @file } | ||
| 691 | end | ||
| 692 | |||
| 693 | should "return the right value when sent #avatar_updated_at" do | ||
| 694 | now = Time.now | ||
| 695 | Time.stubs(:now).returns(now) | ||
| 696 | @dummy.avatar = @file | ||
| 697 | assert_equal now.to_i, @dummy.avatar.updated_at | ||
| 698 | end | ||
| 699 | end | ||
| 700 | |||
| 701 | context "and avatar_content_type column" do | ||
| 702 | setup do | ||
| 703 | ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string | ||
| 704 | rebuild_class | ||
| 705 | @dummy = Dummy.new | ||
| 706 | end | ||
| 707 | |||
| 708 | should "not error when assigned an attachment" do | ||
| 709 | assert_nothing_raised { @dummy.avatar = @file } | ||
| 710 | end | ||
| 711 | |||
| 712 | should "return the right value when sent #avatar_content_type" do | ||
| 713 | @dummy.avatar = @file | ||
| 714 | assert_equal "image/png", @dummy.avatar.content_type | ||
| 715 | end | ||
| 716 | end | ||
| 717 | |||
| 718 | context "and avatar_file_size column" do | ||
| 719 | setup do | ||
| 720 | ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer | ||
| 721 | rebuild_class | ||
| 722 | @dummy = Dummy.new | ||
| 723 | end | ||
| 724 | |||
| 725 | should "not error when assigned an attachment" do | ||
| 726 | assert_nothing_raised { @dummy.avatar = @file } | ||
| 727 | end | ||
| 728 | |||
| 729 | should "return the right value when sent #avatar_file_size" do | ||
| 730 | @dummy.avatar = @file | ||
| 731 | assert_equal @file.size, @dummy.avatar.size | ||
| 732 | end | ||
| 733 | |||
| 734 | should "return the right value when saved, reloaded, and sent #avatar_file_size" do | ||
| 735 | @dummy.avatar = @file | ||
| 736 | @dummy.save | ||
| 737 | @dummy = Dummy.find(@dummy.id) | ||
| 738 | assert_equal @file.size, @dummy.avatar.size | ||
| 739 | end | ||
| 740 | end | ||
| 741 | end | ||
| 742 | end | ||
diff --git a/vendor/plugins/paperclip/test/fixtures/12k.png b/vendor/plugins/paperclip/test/fixtures/12k.png new file mode 100644 index 0000000..f819d45 --- /dev/null +++ b/vendor/plugins/paperclip/test/fixtures/12k.png | |||
| Binary files differ | |||
diff --git a/vendor/plugins/paperclip/test/fixtures/50x50.png b/vendor/plugins/paperclip/test/fixtures/50x50.png new file mode 100644 index 0000000..63f5646 --- /dev/null +++ b/vendor/plugins/paperclip/test/fixtures/50x50.png | |||
| Binary files differ | |||
diff --git a/vendor/plugins/paperclip/test/fixtures/5k.png b/vendor/plugins/paperclip/test/fixtures/5k.png new file mode 100644 index 0000000..75d9f04 --- /dev/null +++ b/vendor/plugins/paperclip/test/fixtures/5k.png | |||
| Binary files differ | |||
diff --git a/vendor/plugins/paperclip/test/fixtures/bad.png b/vendor/plugins/paperclip/test/fixtures/bad.png new file mode 100644 index 0000000..7ba4f07 --- /dev/null +++ b/vendor/plugins/paperclip/test/fixtures/bad.png | |||
| @@ -0,0 +1 @@ | |||
| This is not an image. | |||
diff --git a/vendor/plugins/paperclip/test/fixtures/text.txt b/vendor/plugins/paperclip/test/fixtures/text.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vendor/plugins/paperclip/test/fixtures/text.txt | |||
diff --git a/vendor/plugins/paperclip/test/fixtures/twopage.pdf b/vendor/plugins/paperclip/test/fixtures/twopage.pdf new file mode 100644 index 0000000..0c34a51 --- /dev/null +++ b/vendor/plugins/paperclip/test/fixtures/twopage.pdf | |||
| Binary files differ | |||
diff --git a/vendor/plugins/paperclip/test/geometry_test.rb b/vendor/plugins/paperclip/test/geometry_test.rb new file mode 100644 index 0000000..134372d --- /dev/null +++ b/vendor/plugins/paperclip/test/geometry_test.rb | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class GeometryTest < Test::Unit::TestCase | ||
| 4 | context "Paperclip::Geometry" do | ||
| 5 | should "correctly report its given dimensions" do | ||
| 6 | assert @geo = Paperclip::Geometry.new(1024, 768) | ||
| 7 | assert_equal 1024, @geo.width | ||
| 8 | assert_equal 768, @geo.height | ||
| 9 | end | ||
| 10 | |||
| 11 | should "set height to 0 if height dimension is missing" do | ||
| 12 | assert @geo = Paperclip::Geometry.new(1024) | ||
| 13 | assert_equal 1024, @geo.width | ||
| 14 | assert_equal 0, @geo.height | ||
| 15 | end | ||
| 16 | |||
| 17 | should "set width to 0 if width dimension is missing" do | ||
| 18 | assert @geo = Paperclip::Geometry.new(nil, 768) | ||
| 19 | assert_equal 0, @geo.width | ||
| 20 | assert_equal 768, @geo.height | ||
| 21 | end | ||
| 22 | |||
| 23 | should "be generated from a WxH-formatted string" do | ||
| 24 | assert @geo = Paperclip::Geometry.parse("800x600") | ||
| 25 | assert_equal 800, @geo.width | ||
| 26 | assert_equal 600, @geo.height | ||
| 27 | end | ||
| 28 | |||
| 29 | should "be generated from a xH-formatted string" do | ||
| 30 | assert @geo = Paperclip::Geometry.parse("x600") | ||
| 31 | assert_equal 0, @geo.width | ||
| 32 | assert_equal 600, @geo.height | ||
| 33 | end | ||
| 34 | |||
| 35 | should "be generated from a Wx-formatted string" do | ||
| 36 | assert @geo = Paperclip::Geometry.parse("800x") | ||
| 37 | assert_equal 800, @geo.width | ||
| 38 | assert_equal 0, @geo.height | ||
| 39 | end | ||
| 40 | |||
| 41 | should "be generated from a W-formatted string" do | ||
| 42 | assert @geo = Paperclip::Geometry.parse("800") | ||
| 43 | assert_equal 800, @geo.width | ||
| 44 | assert_equal 0, @geo.height | ||
| 45 | end | ||
| 46 | |||
| 47 | should "ensure the modifier is nil if not present" do | ||
| 48 | assert @geo = Paperclip::Geometry.parse("123x456") | ||
| 49 | assert_nil @geo.modifier | ||
| 50 | end | ||
| 51 | |||
| 52 | ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod| | ||
| 53 | should "ensure the modifier #{mod.inspect} is preserved" do | ||
| 54 | assert @geo = Paperclip::Geometry.parse("123x456#{mod}") | ||
| 55 | assert_equal mod, @geo.modifier | ||
| 56 | assert_equal "123x456#{mod}", @geo.to_s | ||
| 57 | end | ||
| 58 | end | ||
| 59 | |||
| 60 | ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod| | ||
| 61 | should "ensure the modifier #{mod.inspect} is preserved with no height" do | ||
| 62 | assert @geo = Paperclip::Geometry.parse("123x#{mod}") | ||
| 63 | assert_equal mod, @geo.modifier | ||
| 64 | assert_equal "123#{mod}", @geo.to_s | ||
| 65 | end | ||
| 66 | end | ||
| 67 | |||
| 68 | should "make sure the modifier gets passed during transformation_to" do | ||
| 69 | assert @src = Paperclip::Geometry.parse("123x456") | ||
| 70 | assert @dst = Paperclip::Geometry.parse("123x456>") | ||
| 71 | assert_equal "123x456>", @src.transformation_to(@dst).to_s | ||
| 72 | end | ||
| 73 | |||
| 74 | should "generate correct ImageMagick formatting string for W-formatted string" do | ||
| 75 | assert @geo = Paperclip::Geometry.parse("800") | ||
| 76 | assert_equal "800", @geo.to_s | ||
| 77 | end | ||
| 78 | |||
| 79 | should "generate correct ImageMagick formatting string for Wx-formatted string" do | ||
| 80 | assert @geo = Paperclip::Geometry.parse("800x") | ||
| 81 | assert_equal "800", @geo.to_s | ||
| 82 | end | ||
| 83 | |||
| 84 | should "generate correct ImageMagick formatting string for xH-formatted string" do | ||
| 85 | assert @geo = Paperclip::Geometry.parse("x600") | ||
| 86 | assert_equal "x600", @geo.to_s | ||
| 87 | end | ||
| 88 | |||
| 89 | should "generate correct ImageMagick formatting string for WxH-formatted string" do | ||
| 90 | assert @geo = Paperclip::Geometry.parse("800x600") | ||
| 91 | assert_equal "800x600", @geo.to_s | ||
| 92 | end | ||
| 93 | |||
| 94 | should "be generated from a file" do | ||
| 95 | file = File.join(File.dirname(__FILE__), "fixtures", "5k.png") | ||
| 96 | file = File.new(file, 'rb') | ||
| 97 | assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) } | ||
| 98 | assert @geo.height > 0 | ||
| 99 | assert @geo.width > 0 | ||
| 100 | end | ||
| 101 | |||
| 102 | should "be generated from a file path" do | ||
| 103 | file = File.join(File.dirname(__FILE__), "fixtures", "5k.png") | ||
| 104 | assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) } | ||
| 105 | assert @geo.height > 0 | ||
| 106 | assert @geo.width > 0 | ||
| 107 | end | ||
| 108 | |||
| 109 | should "not generate from a bad file" do | ||
| 110 | file = "/home/This File Does Not Exist.omg" | ||
| 111 | assert_raise(Paperclip::NotIdentifiedByImageMagickError){ @geo = Paperclip::Geometry.from_file(file) } | ||
| 112 | end | ||
| 113 | |||
| 114 | [['vertical', 900, 1440, true, false, false, 1440, 900, 0.625], | ||
| 115 | ['horizontal', 1024, 768, false, true, false, 1024, 768, 1.3333], | ||
| 116 | ['square', 100, 100, false, false, true, 100, 100, 1]].each do |args| | ||
| 117 | context "performing calculations on a #{args[0]} viewport" do | ||
| 118 | setup do | ||
| 119 | @geo = Paperclip::Geometry.new(args[1], args[2]) | ||
| 120 | end | ||
| 121 | |||
| 122 | should "#{args[3] ? "" : "not"} be vertical" do | ||
| 123 | assert_equal args[3], @geo.vertical? | ||
| 124 | end | ||
| 125 | |||
| 126 | should "#{args[4] ? "" : "not"} be horizontal" do | ||
| 127 | assert_equal args[4], @geo.horizontal? | ||
| 128 | end | ||
| 129 | |||
| 130 | should "#{args[5] ? "" : "not"} be square" do | ||
| 131 | assert_equal args[5], @geo.square? | ||
| 132 | end | ||
| 133 | |||
| 134 | should "report that #{args[6]} is the larger dimension" do | ||
| 135 | assert_equal args[6], @geo.larger | ||
| 136 | end | ||
| 137 | |||
| 138 | should "report that #{args[7]} is the smaller dimension" do | ||
| 139 | assert_equal args[7], @geo.smaller | ||
| 140 | end | ||
| 141 | |||
| 142 | should "have an aspect ratio of #{args[8]}" do | ||
| 143 | assert_in_delta args[8], @geo.aspect, 0.0001 | ||
| 144 | end | ||
| 145 | end | ||
| 146 | end | ||
| 147 | |||
| 148 | [[ [1000, 100], [64, 64], "x64", "64x64+288+0" ], | ||
| 149 | [ [100, 1000], [50, 950], "x950", "50x950+22+0" ], | ||
| 150 | [ [100, 1000], [50, 25], "50x", "50x25+0+237" ]]. each do |args| | ||
| 151 | context "of #{args[0].inspect} and given a Geometry #{args[1].inspect} and sent transform_to" do | ||
| 152 | setup do | ||
| 153 | @geo = Paperclip::Geometry.new(*args[0]) | ||
| 154 | @dst = Paperclip::Geometry.new(*args[1]) | ||
| 155 | @scale, @crop = @geo.transformation_to @dst, true | ||
| 156 | end | ||
| 157 | |||
| 158 | should "be able to return the correct scaling transformation geometry #{args[2]}" do | ||
| 159 | assert_equal args[2], @scale | ||
| 160 | end | ||
| 161 | |||
| 162 | should "be able to return the correct crop transformation geometry #{args[3]}" do | ||
| 163 | assert_equal args[3], @crop | ||
| 164 | end | ||
| 165 | end | ||
| 166 | end | ||
| 167 | end | ||
| 168 | end | ||
diff --git a/vendor/plugins/paperclip/test/helper.rb b/vendor/plugins/paperclip/test/helper.rb new file mode 100644 index 0000000..3e7e6a1 --- /dev/null +++ b/vendor/plugins/paperclip/test/helper.rb | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | require 'rubygems' | ||
| 2 | require 'test/unit' | ||
| 3 | gem 'thoughtbot-shoulda', ">= 2.9.0" | ||
| 4 | require 'shoulda' | ||
| 5 | require 'mocha' | ||
| 6 | require 'tempfile' | ||
| 7 | |||
| 8 | gem 'sqlite3-ruby' | ||
| 9 | |||
| 10 | require 'active_record' | ||
| 11 | require 'active_support' | ||
| 12 | begin | ||
| 13 | require 'ruby-debug' | ||
| 14 | rescue LoadError | ||
| 15 | puts "ruby-debug not loaded" | ||
| 16 | end | ||
| 17 | |||
| 18 | ROOT = File.join(File.dirname(__FILE__), '..') | ||
| 19 | RAILS_ROOT = ROOT | ||
| 20 | |||
| 21 | $LOAD_PATH << File.join(ROOT, 'lib') | ||
| 22 | $LOAD_PATH << File.join(ROOT, 'lib', 'paperclip') | ||
| 23 | |||
| 24 | require File.join(ROOT, 'lib', 'paperclip.rb') | ||
| 25 | |||
| 26 | require 'shoulda_macros/paperclip' | ||
| 27 | |||
| 28 | ENV['RAILS_ENV'] ||= 'test' | ||
| 29 | |||
| 30 | FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures") | ||
| 31 | config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml')) | ||
| 32 | ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log") | ||
| 33 | ActiveRecord::Base.establish_connection(config['test']) | ||
| 34 | |||
| 35 | def reset_class class_name | ||
| 36 | ActiveRecord::Base.send(:include, Paperclip) | ||
| 37 | Object.send(:remove_const, class_name) rescue nil | ||
| 38 | klass = Object.const_set(class_name, Class.new(ActiveRecord::Base)) | ||
| 39 | klass.class_eval{ include Paperclip } | ||
| 40 | klass | ||
| 41 | end | ||
| 42 | |||
| 43 | def reset_table table_name, &block | ||
| 44 | block ||= lambda{ true } | ||
| 45 | ActiveRecord::Base.connection.create_table :dummies, {:force => true}, &block | ||
| 46 | end | ||
| 47 | |||
| 48 | def modify_table table_name, &block | ||
| 49 | ActiveRecord::Base.connection.change_table :dummies, &block | ||
| 50 | end | ||
| 51 | |||
| 52 | def rebuild_model options = {} | ||
| 53 | ActiveRecord::Base.connection.create_table :dummies, :force => true do |table| | ||
| 54 | table.column :other, :string | ||
| 55 | table.column :avatar_file_name, :string | ||
| 56 | table.column :avatar_content_type, :string | ||
| 57 | table.column :avatar_file_size, :integer | ||
| 58 | table.column :avatar_updated_at, :datetime | ||
| 59 | end | ||
| 60 | rebuild_class options | ||
| 61 | end | ||
| 62 | |||
| 63 | def rebuild_class options = {} | ||
| 64 | ActiveRecord::Base.send(:include, Paperclip) | ||
| 65 | Object.send(:remove_const, "Dummy") rescue nil | ||
| 66 | Object.const_set("Dummy", Class.new(ActiveRecord::Base)) | ||
| 67 | Dummy.class_eval do | ||
| 68 | include Paperclip | ||
| 69 | has_attached_file :avatar, options | ||
| 70 | end | ||
| 71 | end | ||
| 72 | |||
| 73 | def temporary_rails_env(new_env) | ||
| 74 | old_env = defined?(RAILS_ENV) ? RAILS_ENV : nil | ||
| 75 | silence_warnings do | ||
| 76 | Object.const_set("RAILS_ENV", new_env) | ||
| 77 | end | ||
| 78 | yield | ||
| 79 | silence_warnings do | ||
| 80 | Object.const_set("RAILS_ENV", old_env) | ||
| 81 | end | ||
| 82 | end | ||
diff --git a/vendor/plugins/paperclip/test/integration_test.rb b/vendor/plugins/paperclip/test/integration_test.rb new file mode 100644 index 0000000..f7014ac --- /dev/null +++ b/vendor/plugins/paperclip/test/integration_test.rb | |||
| @@ -0,0 +1,481 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class IntegrationTest < Test::Unit::TestCase | ||
| 4 | context "Many models at once" do | ||
| 5 | setup do | ||
| 6 | rebuild_model | ||
| 7 | @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb') | ||
| 8 | 300.times do |i| | ||
| 9 | Dummy.create! :avatar => @file | ||
| 10 | end | ||
| 11 | end | ||
| 12 | |||
| 13 | should "not exceed the open file limit" do | ||
| 14 | assert_nothing_raised do | ||
| 15 | dummies = Dummy.find(:all) | ||
| 16 | dummies.each { |dummy| dummy.avatar } | ||
| 17 | end | ||
| 18 | end | ||
| 19 | end | ||
| 20 | |||
| 21 | context "An attachment" do | ||
| 22 | setup do | ||
| 23 | rebuild_model :styles => { :thumb => "50x50#" } | ||
| 24 | @dummy = Dummy.new | ||
| 25 | @file = File.new(File.join(File.dirname(__FILE__), | ||
| 26 | "fixtures", | ||
| 27 | "5k.png"), 'rb') | ||
| 28 | @dummy.avatar = @file | ||
| 29 | assert @dummy.save | ||
| 30 | end | ||
| 31 | |||
| 32 | teardown { @file.close } | ||
| 33 | |||
| 34 | should "create its thumbnails properly" do | ||
| 35 | assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"` | ||
| 36 | end | ||
| 37 | |||
| 38 | context "redefining its attachment styles" do | ||
| 39 | setup do | ||
| 40 | Dummy.class_eval do | ||
| 41 | has_attached_file :avatar, :styles => { :thumb => "150x25#" } | ||
| 42 | has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } } | ||
| 43 | end | ||
| 44 | @d2 = Dummy.find(@dummy.id) | ||
| 45 | @d2.avatar.reprocess! | ||
| 46 | @d2.save | ||
| 47 | end | ||
| 48 | |||
| 49 | should "create its thumbnails properly" do | ||
| 50 | assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"` | ||
| 51 | assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"` | ||
| 52 | end | ||
| 53 | end | ||
| 54 | end | ||
| 55 | |||
| 56 | context "A model that modifies its original" do | ||
| 57 | setup do | ||
| 58 | rebuild_model :styles => { :original => "2x2#" } | ||
| 59 | @dummy = Dummy.new | ||
| 60 | @file = File.new(File.join(File.dirname(__FILE__), | ||
| 61 | "fixtures", | ||
| 62 | "5k.png"), 'rb') | ||
| 63 | @dummy.avatar = @file | ||
| 64 | end | ||
| 65 | |||
| 66 | should "report the file size of the processed file and not the original" do | ||
| 67 | assert_not_equal @file.size, @dummy.avatar.size | ||
| 68 | end | ||
| 69 | |||
| 70 | teardown { @file.close } | ||
| 71 | end | ||
| 72 | |||
| 73 | context "A model with attachments scoped under an id" do | ||
| 74 | setup do | ||
| 75 | rebuild_model :styles => { :large => "100x100", | ||
| 76 | :medium => "50x50" }, | ||
| 77 | :path => ":rails_root/tmp/:id/:attachments/:style.:extension" | ||
| 78 | @dummy = Dummy.new | ||
| 79 | @file = File.new(File.join(File.dirname(__FILE__), | ||
| 80 | "fixtures", | ||
| 81 | "5k.png"), 'rb') | ||
| 82 | @dummy.avatar = @file | ||
| 83 | end | ||
| 84 | |||
| 85 | teardown { @file.close } | ||
| 86 | |||
| 87 | context "when saved" do | ||
| 88 | setup do | ||
| 89 | @dummy.save | ||
| 90 | @saved_path = @dummy.avatar.path(:large) | ||
| 91 | end | ||
| 92 | |||
| 93 | should "have a large file in the right place" do | ||
| 94 | assert File.exists?(@dummy.avatar.path(:large)) | ||
| 95 | end | ||
| 96 | |||
| 97 | context "and deleted" do | ||
| 98 | setup do | ||
| 99 | @dummy.avatar.clear | ||
| 100 | @dummy.save | ||
| 101 | end | ||
| 102 | |||
| 103 | should "not have a large file in the right place anymore" do | ||
| 104 | assert ! File.exists?(@saved_path) | ||
| 105 | end | ||
| 106 | |||
| 107 | should "not have its next two parent directories" do | ||
| 108 | assert ! File.exists?(File.dirname(@saved_path)) | ||
| 109 | assert ! File.exists?(File.dirname(File.dirname(@saved_path))) | ||
| 110 | end | ||
| 111 | |||
| 112 | before_should "not die if an unexpected SystemCallError happens" do | ||
| 113 | FileUtils.stubs(:rmdir).raises(Errno::EPIPE) | ||
| 114 | end | ||
| 115 | end | ||
| 116 | end | ||
| 117 | end | ||
| 118 | |||
| 119 | context "A model with no attachment validation" do | ||
| 120 | setup do | ||
| 121 | rebuild_model :styles => { :large => "300x300>", | ||
| 122 | :medium => "100x100", | ||
| 123 | :thumb => ["32x32#", :gif] }, | ||
| 124 | :default_style => :medium, | ||
| 125 | :url => "/:attachment/:class/:style/:id/:basename.:extension", | ||
| 126 | :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension" | ||
| 127 | @dummy = Dummy.new | ||
| 128 | end | ||
| 129 | |||
| 130 | should "have its definition return false when asked about whiny_thumbnails" do | ||
| 131 | assert ! Dummy.attachment_definitions[:avatar][:whiny_thumbnails] | ||
| 132 | end | ||
| 133 | |||
| 134 | context "when validates_attachment_thumbnails is called" do | ||
| 135 | setup do | ||
| 136 | Dummy.validates_attachment_thumbnails :avatar | ||
| 137 | end | ||
| 138 | |||
| 139 | should "have its definition return true when asked about whiny_thumbnails" do | ||
| 140 | assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails] | ||
| 141 | end | ||
| 142 | end | ||
| 143 | |||
| 144 | context "redefined to have attachment validations" do | ||
| 145 | setup do | ||
| 146 | rebuild_model :styles => { :large => "300x300>", | ||
| 147 | :medium => "100x100", | ||
| 148 | :thumb => ["32x32#", :gif] }, | ||
| 149 | :whiny_thumbnails => true, | ||
| 150 | :default_style => :medium, | ||
| 151 | :url => "/:attachment/:class/:style/:id/:basename.:extension", | ||
| 152 | :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension" | ||
| 153 | end | ||
| 154 | |||
| 155 | should "have its definition return true when asked about whiny_thumbnails" do | ||
| 156 | assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails] | ||
| 157 | end | ||
| 158 | end | ||
| 159 | end | ||
| 160 | |||
| 161 | context "A model with no convert_options setting" do | ||
| 162 | setup do | ||
| 163 | rebuild_model :styles => { :large => "300x300>", | ||
| 164 | :medium => "100x100", | ||
| 165 | :thumb => ["32x32#", :gif] }, | ||
| 166 | :default_style => :medium, | ||
| 167 | :url => "/:attachment/:class/:style/:id/:basename.:extension", | ||
| 168 | :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension" | ||
| 169 | @dummy = Dummy.new | ||
| 170 | end | ||
| 171 | |||
| 172 | should "have its definition return nil when asked about convert_options" do | ||
| 173 | assert ! Dummy.attachment_definitions[:avatar][:convert_options] | ||
| 174 | end | ||
| 175 | |||
| 176 | context "redefined to have convert_options setting" do | ||
| 177 | setup do | ||
| 178 | rebuild_model :styles => { :large => "300x300>", | ||
| 179 | :medium => "100x100", | ||
| 180 | :thumb => ["32x32#", :gif] }, | ||
| 181 | :convert_options => "-strip -depth 8", | ||
| 182 | :default_style => :medium, | ||
| 183 | :url => "/:attachment/:class/:style/:id/:basename.:extension", | ||
| 184 | :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension" | ||
| 185 | end | ||
| 186 | |||
| 187 | should "have its definition return convert_options value when asked about convert_options" do | ||
| 188 | assert_equal "-strip -depth 8", Dummy.attachment_definitions[:avatar][:convert_options] | ||
| 189 | end | ||
| 190 | end | ||
| 191 | end | ||
| 192 | |||
| 193 | context "A model with a filesystem attachment" do | ||
| 194 | setup do | ||
| 195 | rebuild_model :styles => { :large => "300x300>", | ||
| 196 | :medium => "100x100", | ||
| 197 | :thumb => ["32x32#", :gif] }, | ||
| 198 | :whiny_thumbnails => true, | ||
| 199 | :default_style => :medium, | ||
| 200 | :url => "/:attachment/:class/:style/:id/:basename.:extension", | ||
| 201 | :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension" | ||
| 202 | @dummy = Dummy.new | ||
| 203 | @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb') | ||
| 204 | @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb') | ||
| 205 | |||
| 206 | assert @dummy.avatar = @file | ||
| 207 | assert @dummy.valid? | ||
| 208 | assert @dummy.save | ||
| 209 | end | ||
| 210 | |||
| 211 | should "write and delete its files" do | ||
| 212 | [["434x66", :original], | ||
| 213 | ["300x46", :large], | ||
| 214 | ["100x15", :medium], | ||
| 215 | ["32x32", :thumb]].each do |geo, style| | ||
| 216 | cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"] | ||
| 217 | assert_equal geo, `#{cmd}`.chomp, cmd | ||
| 218 | end | ||
| 219 | |||
| 220 | saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) } | ||
| 221 | |||
| 222 | @d2 = Dummy.find(@dummy.id) | ||
| 223 | assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp | ||
| 224 | assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp | ||
| 225 | assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp | ||
| 226 | assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp | ||
| 227 | assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp | ||
| 228 | |||
| 229 | @dummy.avatar = "not a valid file but not nil" | ||
| 230 | assert_equal File.basename(@file.path), @dummy.avatar_file_name | ||
| 231 | assert @dummy.valid? | ||
| 232 | assert @dummy.save | ||
| 233 | |||
| 234 | saved_paths.each do |p| | ||
| 235 | assert File.exists?(p) | ||
| 236 | end | ||
| 237 | |||
| 238 | @dummy.avatar.clear | ||
| 239 | assert_nil @dummy.avatar_file_name | ||
| 240 | assert @dummy.valid? | ||
| 241 | assert @dummy.save | ||
| 242 | |||
| 243 | saved_paths.each do |p| | ||
| 244 | assert ! File.exists?(p) | ||
| 245 | end | ||
| 246 | |||
| 247 | @d2 = Dummy.find(@dummy.id) | ||
| 248 | assert_nil @d2.avatar_file_name | ||
| 249 | end | ||
| 250 | |||
| 251 | should "work exactly the same when new as when reloaded" do | ||
| 252 | @d2 = Dummy.find(@dummy.id) | ||
| 253 | |||
| 254 | assert_equal @dummy.avatar_file_name, @d2.avatar_file_name | ||
| 255 | [:thumb, :medium, :large, :original].each do |style| | ||
| 256 | assert_equal @dummy.avatar.path(style), @d2.avatar.path(style) | ||
| 257 | end | ||
| 258 | |||
| 259 | saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) } | ||
| 260 | |||
| 261 | @d2.avatar.clear | ||
| 262 | assert @d2.save | ||
| 263 | |||
| 264 | saved_paths.each do |p| | ||
| 265 | assert ! File.exists?(p) | ||
| 266 | end | ||
| 267 | end | ||
| 268 | |||
| 269 | should "know the difference between good files, bad files, and not files" do | ||
| 270 | expected = @dummy.avatar.to_file | ||
| 271 | @dummy.avatar = "not a file" | ||
| 272 | assert @dummy.valid? | ||
| 273 | assert_equal expected.path, @dummy.avatar.path | ||
| 274 | expected.close | ||
| 275 | |||
| 276 | @dummy.avatar = @bad_file | ||
| 277 | assert ! @dummy.valid? | ||
| 278 | end | ||
| 279 | |||
| 280 | should "know the difference between good files, bad files, and not files when validating" do | ||
| 281 | Dummy.validates_attachment_presence :avatar | ||
| 282 | @d2 = Dummy.find(@dummy.id) | ||
| 283 | @d2.avatar = @file | ||
| 284 | assert @d2.valid?, @d2.errors.full_messages.inspect | ||
| 285 | @d2.avatar = @bad_file | ||
| 286 | assert ! @d2.valid? | ||
| 287 | end | ||
| 288 | |||
| 289 | should "be able to reload without saving and not have the file disappear" do | ||
| 290 | @dummy.avatar = @file | ||
| 291 | assert @dummy.save | ||
| 292 | @dummy.avatar.clear | ||
| 293 | assert_nil @dummy.avatar_file_name | ||
| 294 | @dummy.reload | ||
| 295 | assert_equal "5k.png", @dummy.avatar_file_name | ||
| 296 | end | ||
| 297 | |||
| 298 | context "that is assigned its file from another Paperclip attachment" do | ||
| 299 | setup do | ||
| 300 | @dummy2 = Dummy.new | ||
| 301 | @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb') | ||
| 302 | assert @dummy2.avatar = @file2 | ||
| 303 | @dummy2.save | ||
| 304 | end | ||
| 305 | |||
| 306 | should "work when assigned a file" do | ||
| 307 | assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`, | ||
| 308 | `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"` | ||
| 309 | |||
| 310 | assert @dummy.avatar = @dummy2.avatar | ||
| 311 | @dummy.save | ||
| 312 | assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`, | ||
| 313 | `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"` | ||
| 314 | end | ||
| 315 | end | ||
| 316 | |||
| 317 | end | ||
| 318 | |||
| 319 | context "A model with an attachments association and a Paperclip attachment" do | ||
| 320 | setup do | ||
| 321 | Dummy.class_eval do | ||
| 322 | has_many :attachments, :class_name => 'Dummy' | ||
| 323 | end | ||
| 324 | |||
| 325 | @dummy = Dummy.new | ||
| 326 | @dummy.avatar = File.new(File.join(File.dirname(__FILE__), | ||
| 327 | "fixtures", | ||
| 328 | "5k.png"), 'rb') | ||
| 329 | end | ||
| 330 | |||
| 331 | should "should not error when saving" do | ||
| 332 | assert_nothing_raised do | ||
| 333 | @dummy.save! | ||
| 334 | end | ||
| 335 | end | ||
| 336 | end | ||
| 337 | |||
| 338 | if ENV['S3_TEST_BUCKET'] | ||
| 339 | def s3_files_for attachment | ||
| 340 | [:thumb, :medium, :large, :original].inject({}) do |files, style| | ||
| 341 | data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp | ||
| 342 | t = Tempfile.new("paperclip-test") | ||
| 343 | t.binmode | ||
| 344 | t.write(data) | ||
| 345 | t.rewind | ||
| 346 | files[style] = t | ||
| 347 | files | ||
| 348 | end | ||
| 349 | end | ||
| 350 | |||
| 351 | def s3_headers_for attachment, style | ||
| 352 | `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head| | ||
| 353 | split_head = head.chomp.split(/\s*:\s*/, 2) | ||
| 354 | h[split_head.first.downcase] = split_head.last unless split_head.empty? | ||
| 355 | h | ||
| 356 | end | ||
| 357 | end | ||
| 358 | |||
| 359 | context "A model with an S3 attachment" do | ||
| 360 | setup do | ||
| 361 | rebuild_model :styles => { :large => "300x300>", | ||
| 362 | :medium => "100x100", | ||
| 363 | :thumb => ["32x32#", :gif] }, | ||
| 364 | :storage => :s3, | ||
| 365 | :whiny_thumbnails => true, | ||
| 366 | # :s3_options => {:logger => Logger.new(StringIO.new)}, | ||
| 367 | :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")), | ||
| 368 | :default_style => :medium, | ||
| 369 | :bucket => ENV['S3_TEST_BUCKET'], | ||
| 370 | :path => ":class/:attachment/:id/:style/:basename.:extension" | ||
| 371 | @dummy = Dummy.new | ||
| 372 | @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb') | ||
| 373 | @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb') | ||
| 374 | |||
| 375 | assert @dummy.avatar = @file | ||
| 376 | assert @dummy.valid? | ||
| 377 | assert @dummy.save | ||
| 378 | |||
| 379 | @files_on_s3 = s3_files_for @dummy.avatar | ||
| 380 | end | ||
| 381 | |||
| 382 | should "write and delete its files" do | ||
| 383 | [["434x66", :original], | ||
| 384 | ["300x46", :large], | ||
| 385 | ["100x15", :medium], | ||
| 386 | ["32x32", :thumb]].each do |geo, style| | ||
| 387 | cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"] | ||
| 388 | assert_equal geo, `#{cmd}`.chomp, cmd | ||
| 389 | end | ||
| 390 | |||
| 391 | @d2 = Dummy.find(@dummy.id) | ||
| 392 | @d2_files = s3_files_for @d2.avatar | ||
| 393 | [["434x66", :original], | ||
| 394 | ["300x46", :large], | ||
| 395 | ["100x15", :medium], | ||
| 396 | ["32x32", :thumb]].each do |geo, style| | ||
| 397 | cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"] | ||
| 398 | assert_equal geo, `#{cmd}`.chomp, cmd | ||
| 399 | end | ||
| 400 | |||
| 401 | @dummy.avatar = "not a valid file but not nil" | ||
| 402 | assert_equal File.basename(@file.path), @dummy.avatar_file_name | ||
| 403 | assert @dummy.valid? | ||
| 404 | assert @dummy.save | ||
| 405 | |||
| 406 | saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) } | ||
| 407 | |||
| 408 | saved_keys.each do |key| | ||
| 409 | assert key.exists? | ||
| 410 | end | ||
| 411 | |||
| 412 | @dummy.avatar.clear | ||
| 413 | assert_nil @dummy.avatar_file_name | ||
| 414 | assert @dummy.valid? | ||
| 415 | assert @dummy.save | ||
| 416 | |||
| 417 | saved_keys.each do |key| | ||
| 418 | assert ! key.exists? | ||
| 419 | end | ||
| 420 | |||
| 421 | @d2 = Dummy.find(@dummy.id) | ||
| 422 | assert_nil @d2.avatar_file_name | ||
| 423 | end | ||
| 424 | |||
| 425 | should "work exactly the same when new as when reloaded" do | ||
| 426 | @d2 = Dummy.find(@dummy.id) | ||
| 427 | |||
| 428 | assert_equal @dummy.avatar_file_name, @d2.avatar_file_name | ||
| 429 | [:thumb, :medium, :large, :original].each do |style| | ||
| 430 | assert_equal @dummy.avatar.to_file(style).to_s, @d2.avatar.to_file(style).to_s | ||
| 431 | end | ||
| 432 | |||
| 433 | saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) } | ||
| 434 | |||
| 435 | @d2.avatar.clear | ||
| 436 | assert @d2.save | ||
| 437 | |||
| 438 | saved_keys.each do |key| | ||
| 439 | assert ! key.exists? | ||
| 440 | end | ||
| 441 | end | ||
| 442 | |||
| 443 | should "know the difference between good files, bad files, not files, and nil" do | ||
| 444 | expected = @dummy.avatar.to_file | ||
| 445 | @dummy.avatar = "not a file" | ||
| 446 | assert @dummy.valid? | ||
| 447 | assert_equal expected.full_name, @dummy.avatar.to_file.full_name | ||
| 448 | |||
| 449 | @dummy.avatar = @bad_file | ||
| 450 | assert ! @dummy.valid? | ||
| 451 | @dummy.avatar = nil | ||
| 452 | assert @dummy.valid? | ||
| 453 | |||
| 454 | Dummy.validates_attachment_presence :avatar | ||
| 455 | @d2 = Dummy.find(@dummy.id) | ||
| 456 | @d2.avatar = @file | ||
| 457 | assert @d2.valid? | ||
| 458 | @d2.avatar = @bad_file | ||
| 459 | assert ! @d2.valid? | ||
| 460 | @d2.avatar = nil | ||
| 461 | assert ! @d2.valid? | ||
| 462 | end | ||
| 463 | |||
| 464 | should "be able to reload without saving and not have the file disappear" do | ||
| 465 | @dummy.avatar = @file | ||
| 466 | assert @dummy.save | ||
| 467 | @dummy.avatar = nil | ||
| 468 | assert_nil @dummy.avatar_file_name | ||
| 469 | @dummy.reload | ||
| 470 | assert_equal "5k.png", @dummy.avatar_file_name | ||
| 471 | end | ||
| 472 | |||
| 473 | should "have the right content type" do | ||
| 474 | headers = s3_headers_for(@dummy.avatar, :original) | ||
| 475 | p headers | ||
| 476 | assert_equal 'image/png', headers['content-type'] | ||
| 477 | end | ||
| 478 | end | ||
| 479 | end | ||
| 480 | end | ||
| 481 | |||
diff --git a/vendor/plugins/paperclip/test/iostream_test.rb b/vendor/plugins/paperclip/test/iostream_test.rb new file mode 100644 index 0000000..97030b5 --- /dev/null +++ b/vendor/plugins/paperclip/test/iostream_test.rb | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class IOStreamTest < Test::Unit::TestCase | ||
| 4 | context "IOStream" do | ||
| 5 | should "be included in IO, File, Tempfile, and StringIO" do | ||
| 6 | [IO, File, Tempfile, StringIO].each do |klass| | ||
| 7 | assert klass.included_modules.include?(IOStream), "Not in #{klass}" | ||
| 8 | end | ||
| 9 | end | ||
| 10 | end | ||
| 11 | |||
| 12 | context "A file" do | ||
| 13 | setup do | ||
| 14 | @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb') | ||
| 15 | end | ||
| 16 | |||
| 17 | teardown { @file.close } | ||
| 18 | |||
| 19 | context "that is sent #stream_to" do | ||
| 20 | |||
| 21 | context "and given a String" do | ||
| 22 | setup do | ||
| 23 | FileUtils.mkdir_p(File.join(ROOT, 'tmp')) | ||
| 24 | assert @result = @file.stream_to(File.join(ROOT, 'tmp', 'iostream.string.test')) | ||
| 25 | end | ||
| 26 | |||
| 27 | should "return a File" do | ||
| 28 | assert @result.is_a?(File) | ||
| 29 | end | ||
| 30 | |||
| 31 | should "contain the same data as the original file" do | ||
| 32 | @file.rewind; @result.rewind | ||
| 33 | assert_equal @file.read, @result.read | ||
| 34 | end | ||
| 35 | end | ||
| 36 | |||
| 37 | context "and given a Tempfile" do | ||
| 38 | setup do | ||
| 39 | tempfile = Tempfile.new('iostream.test') | ||
| 40 | tempfile.binmode | ||
| 41 | assert @result = @file.stream_to(tempfile) | ||
| 42 | end | ||
| 43 | |||
| 44 | should "return a Tempfile" do | ||
| 45 | assert @result.is_a?(Tempfile) | ||
| 46 | end | ||
| 47 | |||
| 48 | should "contain the same data as the original file" do | ||
| 49 | @file.rewind; @result.rewind | ||
| 50 | assert_equal @file.read, @result.read | ||
| 51 | end | ||
| 52 | end | ||
| 53 | |||
| 54 | end | ||
| 55 | |||
| 56 | context "that is sent #to_tempfile" do | ||
| 57 | setup do | ||
| 58 | assert @tempfile = @file.to_tempfile | ||
| 59 | end | ||
| 60 | |||
| 61 | should "convert it to a Tempfile" do | ||
| 62 | assert @tempfile.is_a?(Tempfile) | ||
| 63 | end | ||
| 64 | |||
| 65 | should "have the Tempfile contain the same data as the file" do | ||
| 66 | @file.rewind; @tempfile.rewind | ||
| 67 | assert_equal @file.read, @tempfile.read | ||
| 68 | end | ||
| 69 | end | ||
| 70 | end | ||
| 71 | end | ||
diff --git a/vendor/plugins/paperclip/test/matchers/have_attached_file_matcher_test.rb b/vendor/plugins/paperclip/test/matchers/have_attached_file_matcher_test.rb new file mode 100644 index 0000000..b29ec37 --- /dev/null +++ b/vendor/plugins/paperclip/test/matchers/have_attached_file_matcher_test.rb | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class HaveAttachedFileMatcherTest < Test::Unit::TestCase | ||
| 4 | context "have_attached_file" do | ||
| 5 | setup do | ||
| 6 | @dummy_class = reset_class "Dummy" | ||
| 7 | reset_table "dummies" | ||
| 8 | @matcher = self.class.have_attached_file(:avatar) | ||
| 9 | end | ||
| 10 | |||
| 11 | should "reject a class with no attachment" do | ||
| 12 | assert_rejects @matcher, @dummy_class | ||
| 13 | end | ||
| 14 | |||
| 15 | should "accept a class with an attachment" do | ||
| 16 | modify_table("dummies"){|d| d.string :avatar_file_name } | ||
| 17 | @dummy_class.has_attached_file :avatar | ||
| 18 | assert_accepts @matcher, @dummy_class | ||
| 19 | end | ||
| 20 | end | ||
| 21 | end | ||
diff --git a/vendor/plugins/paperclip/test/matchers/validate_attachment_content_type_matcher_test.rb b/vendor/plugins/paperclip/test/matchers/validate_attachment_content_type_matcher_test.rb new file mode 100644 index 0000000..241eb5f --- /dev/null +++ b/vendor/plugins/paperclip/test/matchers/validate_attachment_content_type_matcher_test.rb | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class ValidateAttachmentContentTypeMatcherTest < Test::Unit::TestCase | ||
| 4 | context "validate_attachment_content_type" do | ||
| 5 | setup do | ||
| 6 | reset_table("dummies") do |d| | ||
| 7 | d.string :avatar_file_name | ||
| 8 | end | ||
| 9 | @dummy_class = reset_class "Dummy" | ||
| 10 | @dummy_class.has_attached_file :avatar | ||
| 11 | @matcher = self.class.validate_attachment_content_type(:avatar). | ||
| 12 | allowing(%w(image/png image/jpeg)). | ||
| 13 | rejecting(%w(audio/mp3 application/octet-stream)) | ||
| 14 | end | ||
| 15 | |||
| 16 | should "reject a class with no validation" do | ||
| 17 | assert_rejects @matcher, @dummy_class | ||
| 18 | end | ||
| 19 | |||
| 20 | should "reject a class with a validation that doesn't match" do | ||
| 21 | @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*} | ||
| 22 | assert_rejects @matcher, @dummy_class | ||
| 23 | end | ||
| 24 | |||
| 25 | should "accept a class with a validation" do | ||
| 26 | @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*} | ||
| 27 | assert_accepts @matcher, @dummy_class | ||
| 28 | end | ||
| 29 | end | ||
| 30 | end | ||
diff --git a/vendor/plugins/paperclip/test/matchers/validate_attachment_presence_matcher_test.rb b/vendor/plugins/paperclip/test/matchers/validate_attachment_presence_matcher_test.rb new file mode 100644 index 0000000..860a760 --- /dev/null +++ b/vendor/plugins/paperclip/test/matchers/validate_attachment_presence_matcher_test.rb | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class ValidateAttachmentPresenceMatcherTest < Test::Unit::TestCase | ||
| 4 | context "validate_attachment_presence" do | ||
| 5 | setup do | ||
| 6 | reset_table("dummies"){|d| d.string :avatar_file_name } | ||
| 7 | @dummy_class = reset_class "Dummy" | ||
| 8 | @dummy_class.has_attached_file :avatar | ||
| 9 | @matcher = self.class.validate_attachment_presence(:avatar) | ||
| 10 | end | ||
| 11 | |||
| 12 | should "reject a class with no validation" do | ||
| 13 | assert_rejects @matcher, @dummy_class | ||
| 14 | end | ||
| 15 | |||
| 16 | should "accept a class with a validation" do | ||
| 17 | @dummy_class.validates_attachment_presence :avatar | ||
| 18 | assert_accepts @matcher, @dummy_class | ||
| 19 | end | ||
| 20 | end | ||
| 21 | end | ||
diff --git a/vendor/plugins/paperclip/test/matchers/validate_attachment_size_matcher_test.rb b/vendor/plugins/paperclip/test/matchers/validate_attachment_size_matcher_test.rb new file mode 100644 index 0000000..7e4c9b4 --- /dev/null +++ b/vendor/plugins/paperclip/test/matchers/validate_attachment_size_matcher_test.rb | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class ValidateAttachmentSizeMatcherTest < Test::Unit::TestCase | ||
| 4 | context "validate_attachment_size" do | ||
| 5 | setup do | ||
| 6 | reset_table("dummies") do |d| | ||
| 7 | d.string :avatar_file_name | ||
| 8 | end | ||
| 9 | @dummy_class = reset_class "Dummy" | ||
| 10 | @dummy_class.has_attached_file :avatar | ||
| 11 | end | ||
| 12 | |||
| 13 | context "of limited size" do | ||
| 14 | setup{ @matcher = self.class.validate_attachment_size(:avatar).in(256..1024) } | ||
| 15 | |||
| 16 | should "reject a class with no validation" do | ||
| 17 | assert_rejects @matcher, @dummy_class | ||
| 18 | end | ||
| 19 | |||
| 20 | should "reject a class with a validation that's too high" do | ||
| 21 | @dummy_class.validates_attachment_size :avatar, :in => 256..2048 | ||
| 22 | assert_rejects @matcher, @dummy_class | ||
| 23 | end | ||
| 24 | |||
| 25 | should "reject a class with a validation that's too low" do | ||
| 26 | @dummy_class.validates_attachment_size :avatar, :in => 0..1024 | ||
| 27 | assert_rejects @matcher, @dummy_class | ||
| 28 | end | ||
| 29 | |||
| 30 | should "accept a class with a validation that matches" do | ||
| 31 | @dummy_class.validates_attachment_size :avatar, :in => 256..1024 | ||
| 32 | assert_accepts @matcher, @dummy_class | ||
| 33 | end | ||
| 34 | end | ||
| 35 | |||
| 36 | context "validates_attachment_size with infinite range" do | ||
| 37 | setup{ @matcher = self.class.validate_attachment_size(:avatar) } | ||
| 38 | |||
| 39 | should "accept a class with an upper limit" do | ||
| 40 | @dummy_class.validates_attachment_size :avatar, :less_than => 1 | ||
| 41 | assert_accepts @matcher, @dummy_class | ||
| 42 | end | ||
| 43 | |||
| 44 | should "accept a class with no upper limit" do | ||
| 45 | @dummy_class.validates_attachment_size :avatar, :greater_than => 1 | ||
| 46 | assert_accepts @matcher, @dummy_class | ||
| 47 | end | ||
| 48 | end | ||
| 49 | end | ||
| 50 | end | ||
diff --git a/vendor/plugins/paperclip/test/paperclip_test.rb b/vendor/plugins/paperclip/test/paperclip_test.rb new file mode 100644 index 0000000..8365649 --- /dev/null +++ b/vendor/plugins/paperclip/test/paperclip_test.rb | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class PaperclipTest < Test::Unit::TestCase | ||
| 4 | [:image_magick_path, :convert_path].each do |path| | ||
| 5 | context "Calling Paperclip.run with an #{path} specified" do | ||
| 6 | setup do | ||
| 7 | Paperclip.options[:image_magick_path] = nil | ||
| 8 | Paperclip.options[:convert_path] = nil | ||
| 9 | Paperclip.options[path] = "/usr/bin" | ||
| 10 | end | ||
| 11 | |||
| 12 | should "execute the right command" do | ||
| 13 | Paperclip.expects(:path_for_command).with("convert").returns("/usr/bin/convert") | ||
| 14 | Paperclip.expects(:bit_bucket).returns("/dev/null") | ||
| 15 | Paperclip.expects(:"`").with("/usr/bin/convert one.jpg two.jpg 2>/dev/null") | ||
| 16 | Paperclip.run("convert", "one.jpg two.jpg") | ||
| 17 | end | ||
| 18 | end | ||
| 19 | end | ||
| 20 | |||
| 21 | context "Calling Paperclip.run with no path specified" do | ||
| 22 | setup do | ||
| 23 | Paperclip.options[:image_magick_path] = nil | ||
| 24 | Paperclip.options[:convert_path] = nil | ||
| 25 | end | ||
| 26 | |||
| 27 | should "execute the right command" do | ||
| 28 | Paperclip.expects(:path_for_command).with("convert").returns("convert") | ||
| 29 | Paperclip.expects(:bit_bucket).returns("/dev/null") | ||
| 30 | Paperclip.expects(:"`").with("convert one.jpg two.jpg 2>/dev/null") | ||
| 31 | Paperclip.run("convert", "one.jpg two.jpg") | ||
| 32 | end | ||
| 33 | end | ||
| 34 | |||
| 35 | should "raise when sent #processor and the name of a class that exists but isn't a subclass of Processor" do | ||
| 36 | assert_raises(Paperclip::PaperclipError){ Paperclip.processor(:attachment) } | ||
| 37 | end | ||
| 38 | |||
| 39 | should "raise when sent #processor and the name of a class that doesn't exist" do | ||
| 40 | assert_raises(NameError){ Paperclip.processor(:boogey_man) } | ||
| 41 | end | ||
| 42 | |||
| 43 | should "return a class when sent #processor and the name of a class under Paperclip" do | ||
| 44 | assert_equal ::Paperclip::Thumbnail, Paperclip.processor(:thumbnail) | ||
| 45 | end | ||
| 46 | |||
| 47 | context "Paperclip.bit_bucket" do | ||
| 48 | context "on systems without /dev/null" do | ||
| 49 | setup do | ||
| 50 | File.expects(:exists?).with("/dev/null").returns(false) | ||
| 51 | end | ||
| 52 | |||
| 53 | should "return 'NUL'" do | ||
| 54 | assert_equal "NUL", Paperclip.bit_bucket | ||
| 55 | end | ||
| 56 | end | ||
| 57 | |||
| 58 | context "on systems with /dev/null" do | ||
| 59 | setup do | ||
| 60 | File.expects(:exists?).with("/dev/null").returns(true) | ||
| 61 | end | ||
| 62 | |||
| 63 | should "return '/dev/null'" do | ||
| 64 | assert_equal "/dev/null", Paperclip.bit_bucket | ||
| 65 | end | ||
| 66 | end | ||
| 67 | end | ||
| 68 | |||
| 69 | context "An ActiveRecord model with an 'avatar' attachment" do | ||
| 70 | setup do | ||
| 71 | rebuild_model :path => "tmp/:class/omg/:style.:extension" | ||
| 72 | @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb') | ||
| 73 | end | ||
| 74 | |||
| 75 | teardown { @file.close } | ||
| 76 | |||
| 77 | should "not error when trying to also create a 'blah' attachment" do | ||
| 78 | assert_nothing_raised do | ||
| 79 | Dummy.class_eval do | ||
| 80 | has_attached_file :blah | ||
| 81 | end | ||
| 82 | end | ||
| 83 | end | ||
| 84 | |||
| 85 | context "that is attr_protected" do | ||
| 86 | setup do | ||
| 87 | Dummy.class_eval do | ||
| 88 | attr_protected :avatar | ||
| 89 | end | ||
| 90 | @dummy = Dummy.new | ||
| 91 | end | ||
| 92 | |||
| 93 | should "not assign the avatar on mass-set" do | ||
| 94 | @dummy.attributes = { :other => "I'm set!", | ||
| 95 | :avatar => @file } | ||
| 96 | |||
| 97 | assert_equal "I'm set!", @dummy.other | ||
| 98 | assert ! @dummy.avatar? | ||
| 99 | end | ||
| 100 | |||
| 101 | should "still allow assigment on normal set" do | ||
| 102 | @dummy.other = "I'm set!" | ||
| 103 | @dummy.avatar = @file | ||
| 104 | |||
| 105 | assert_equal "I'm set!", @dummy.other | ||
| 106 | assert @dummy.avatar? | ||
| 107 | end | ||
| 108 | end | ||
| 109 | |||
| 110 | context "with a subclass" do | ||
| 111 | setup do | ||
| 112 | class ::SubDummy < Dummy; end | ||
| 113 | end | ||
| 114 | |||
| 115 | should "be able to use the attachment from the subclass" do | ||
| 116 | assert_nothing_raised do | ||
| 117 | @subdummy = SubDummy.create(:avatar => @file) | ||
| 118 | end | ||
| 119 | end | ||
| 120 | |||
| 121 | should "be able to see the attachment definition from the subclass's class" do | ||
| 122 | assert_equal "tmp/:class/omg/:style.:extension", SubDummy.attachment_definitions[:avatar][:path] | ||
| 123 | end | ||
| 124 | |||
| 125 | teardown do | ||
| 126 | Object.send(:remove_const, "SubDummy") rescue nil | ||
| 127 | end | ||
| 128 | end | ||
| 129 | |||
| 130 | should "have an #avatar method" do | ||
| 131 | assert Dummy.new.respond_to?(:avatar) | ||
| 132 | end | ||
| 133 | |||
| 134 | should "have an #avatar= method" do | ||
| 135 | assert Dummy.new.respond_to?(:avatar=) | ||
| 136 | end | ||
| 137 | |||
| 138 | context "that is valid" do | ||
| 139 | setup do | ||
| 140 | @dummy = Dummy.new | ||
| 141 | @dummy.avatar = @file | ||
| 142 | end | ||
| 143 | |||
| 144 | should "be valid" do | ||
| 145 | assert @dummy.valid? | ||
| 146 | end | ||
| 147 | |||
| 148 | context "then has a validation added that makes it invalid" do | ||
| 149 | setup do | ||
| 150 | assert @dummy.save | ||
| 151 | Dummy.class_eval do | ||
| 152 | validates_attachment_content_type :avatar, :content_type => ["text/plain"] | ||
| 153 | end | ||
| 154 | @dummy2 = Dummy.find(@dummy.id) | ||
| 155 | end | ||
| 156 | |||
| 157 | should "be invalid when reloaded" do | ||
| 158 | assert ! @dummy2.valid?, @dummy2.errors.inspect | ||
| 159 | end | ||
| 160 | |||
| 161 | should "be able to call #valid? twice without having duplicate errors" do | ||
| 162 | @dummy2.avatar.valid? | ||
| 163 | first_errors = @dummy2.avatar.errors | ||
| 164 | @dummy2.avatar.valid? | ||
| 165 | assert_equal first_errors, @dummy2.avatar.errors | ||
| 166 | end | ||
| 167 | end | ||
| 168 | end | ||
| 169 | |||
| 170 | def self.should_validate validation, options, valid_file, invalid_file | ||
| 171 | context "with #{validation} validation and #{options.inspect} options" do | ||
| 172 | setup do | ||
| 173 | Dummy.send(:"validates_attachment_#{validation}", :avatar, options) | ||
| 174 | @dummy = Dummy.new | ||
| 175 | end | ||
| 176 | context "and assigning nil" do | ||
| 177 | setup do | ||
| 178 | @dummy.avatar = nil | ||
| 179 | @dummy.valid? | ||
| 180 | end | ||
| 181 | if validation == :presence | ||
| 182 | should "have an error on the attachment" do | ||
| 183 | assert @dummy.errors.on(:avatar) | ||
| 184 | end | ||
| 185 | else | ||
| 186 | should "not have an error on the attachment" do | ||
| 187 | assert_nil @dummy.errors.on(:avatar) | ||
| 188 | end | ||
| 189 | end | ||
| 190 | end | ||
| 191 | context "and assigned a valid file" do | ||
| 192 | setup do | ||
| 193 | @dummy.avatar = valid_file | ||
| 194 | @dummy.valid? | ||
| 195 | end | ||
| 196 | should "not have an error when assigned a valid file" do | ||
| 197 | assert ! @dummy.avatar.errors.key?(validation) | ||
| 198 | end | ||
| 199 | should "not have an error on the attachment" do | ||
| 200 | assert_nil @dummy.errors.on(:avatar) | ||
| 201 | end | ||
| 202 | end | ||
| 203 | context "and assigned an invalid file" do | ||
| 204 | setup do | ||
| 205 | @dummy.avatar = invalid_file | ||
| 206 | @dummy.valid? | ||
| 207 | end | ||
| 208 | should "have an error when assigned a valid file" do | ||
| 209 | assert_not_nil @dummy.avatar.errors[validation] | ||
| 210 | end | ||
| 211 | should "have an error on the attachment" do | ||
| 212 | assert @dummy.errors.on(:avatar) | ||
| 213 | end | ||
| 214 | end | ||
| 215 | end | ||
| 216 | end | ||
| 217 | |||
| 218 | [[:presence, {}, "5k.png", nil], | ||
| 219 | [:size, {:in => 1..10240}, nil, "12k.png"], | ||
| 220 | [:size, {:less_than => 10240}, "5k.png", "12k.png"], | ||
| 221 | [:size, {:greater_than => 8096}, "12k.png", "5k.png"], | ||
| 222 | [:content_type, {:content_type => "image/png"}, "5k.png", "text.txt"], | ||
| 223 | [:content_type, {:content_type => "text/plain"}, "text.txt", "5k.png"], | ||
| 224 | [:content_type, {:content_type => %r{image/.*}}, "5k.png", "text.txt"]].each do |args| | ||
| 225 | validation, options, valid_file, invalid_file = args | ||
| 226 | valid_file &&= File.open(File.join(FIXTURES_DIR, valid_file), "rb") | ||
| 227 | invalid_file &&= File.open(File.join(FIXTURES_DIR, invalid_file), "rb") | ||
| 228 | |||
| 229 | should_validate validation, options, valid_file, invalid_file | ||
| 230 | end | ||
| 231 | |||
| 232 | end | ||
| 233 | end | ||
diff --git a/vendor/plugins/paperclip/test/processor_test.rb b/vendor/plugins/paperclip/test/processor_test.rb new file mode 100644 index 0000000..a05f0a9 --- /dev/null +++ b/vendor/plugins/paperclip/test/processor_test.rb | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class ProcessorTest < Test::Unit::TestCase | ||
| 4 | should "instantiate and call #make when sent #make to the class" do | ||
| 5 | processor = mock | ||
| 6 | processor.expects(:make).with() | ||
| 7 | Paperclip::Processor.expects(:new).with(:one, :two, :three).returns(processor) | ||
| 8 | Paperclip::Processor.make(:one, :two, :three) | ||
| 9 | end | ||
| 10 | end | ||
diff --git a/vendor/plugins/paperclip/test/storage_test.rb b/vendor/plugins/paperclip/test/storage_test.rb new file mode 100644 index 0000000..e2ed8a4 --- /dev/null +++ b/vendor/plugins/paperclip/test/storage_test.rb | |||
| @@ -0,0 +1,277 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class StorageTest < Test::Unit::TestCase | ||
| 4 | context "Parsing S3 credentials" do | ||
| 5 | setup do | ||
| 6 | rebuild_model :storage => :s3, | ||
| 7 | :bucket => "testing", | ||
| 8 | :s3_credentials => {:not => :important} | ||
| 9 | |||
| 10 | @dummy = Dummy.new | ||
| 11 | @avatar = @dummy.avatar | ||
| 12 | |||
| 13 | @current_env = ENV['RAILS_ENV'] | ||
| 14 | end | ||
| 15 | |||
| 16 | teardown do | ||
| 17 | ENV['RAILS_ENV'] = @current_env | ||
| 18 | end | ||
| 19 | |||
| 20 | should "get the correct credentials when RAILS_ENV is production" do | ||
| 21 | ENV['RAILS_ENV'] = 'production' | ||
| 22 | assert_equal({:key => "12345"}, | ||
| 23 | @avatar.parse_credentials('production' => {:key => '12345'}, | ||
| 24 | :development => {:key => "54321"})) | ||
| 25 | end | ||
| 26 | |||
| 27 | should "get the correct credentials when RAILS_ENV is development" do | ||
| 28 | ENV['RAILS_ENV'] = 'development' | ||
| 29 | assert_equal({:key => "54321"}, | ||
| 30 | @avatar.parse_credentials('production' => {:key => '12345'}, | ||
| 31 | :development => {:key => "54321"})) | ||
| 32 | end | ||
| 33 | |||
| 34 | should "return the argument if the key does not exist" do | ||
| 35 | ENV['RAILS_ENV'] = "not really an env" | ||
| 36 | assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345")) | ||
| 37 | end | ||
| 38 | end | ||
| 39 | |||
| 40 | context "" do | ||
| 41 | setup do | ||
| 42 | rebuild_model :storage => :s3, | ||
| 43 | :s3_credentials => {}, | ||
| 44 | :bucket => "bucket", | ||
| 45 | :path => ":attachment/:basename.:extension", | ||
| 46 | :url => ":s3_path_url" | ||
| 47 | @dummy = Dummy.new | ||
| 48 | @dummy.avatar = StringIO.new(".") | ||
| 49 | end | ||
| 50 | |||
| 51 | should "return a url based on an S3 path" do | ||
| 52 | assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url | ||
| 53 | end | ||
| 54 | end | ||
| 55 | context "" do | ||
| 56 | setup do | ||
| 57 | rebuild_model :storage => :s3, | ||
| 58 | :s3_credentials => {}, | ||
| 59 | :bucket => "bucket", | ||
| 60 | :path => ":attachment/:basename.:extension", | ||
| 61 | :url => ":s3_domain_url" | ||
| 62 | @dummy = Dummy.new | ||
| 63 | @dummy.avatar = StringIO.new(".") | ||
| 64 | end | ||
| 65 | |||
| 66 | should "return a url based on an S3 subdomain" do | ||
| 67 | assert_match %r{^http://bucket.s3.amazonaws.com/avatars/stringio.txt}, @dummy.avatar.url | ||
| 68 | end | ||
| 69 | end | ||
| 70 | context "" do | ||
| 71 | setup do | ||
| 72 | rebuild_model :storage => :s3, | ||
| 73 | :s3_credentials => { | ||
| 74 | :production => { :bucket => "prod_bucket" }, | ||
| 75 | :development => { :bucket => "dev_bucket" } | ||
| 76 | }, | ||
| 77 | :s3_host_alias => "something.something.com", | ||
| 78 | :path => ":attachment/:basename.:extension", | ||
| 79 | :url => ":s3_alias_url" | ||
| 80 | @dummy = Dummy.new | ||
| 81 | @dummy.avatar = StringIO.new(".") | ||
| 82 | end | ||
| 83 | |||
| 84 | should "return a url based on the host_alias" do | ||
| 85 | assert_match %r{^http://something.something.com/avatars/stringio.txt}, @dummy.avatar.url | ||
| 86 | end | ||
| 87 | end | ||
| 88 | |||
| 89 | context "Parsing S3 credentials with a bucket in them" do | ||
| 90 | setup do | ||
| 91 | rebuild_model :storage => :s3, | ||
| 92 | :s3_credentials => { | ||
| 93 | :production => { :bucket => "prod_bucket" }, | ||
| 94 | :development => { :bucket => "dev_bucket" } | ||
| 95 | } | ||
| 96 | @dummy = Dummy.new | ||
| 97 | end | ||
| 98 | |||
| 99 | should "get the right bucket in production", :before => lambda{ ENV.expects(:[]).returns('production') } do | ||
| 100 | assert_equal "prod_bucket", @dummy.avatar.bucket_name | ||
| 101 | end | ||
| 102 | |||
| 103 | should "get the right bucket in development", :before => lambda{ ENV.expects(:[]).returns('development') } do | ||
| 104 | assert_equal "dev_bucket", @dummy.avatar.bucket_name | ||
| 105 | end | ||
| 106 | end | ||
| 107 | |||
| 108 | context "An attachment with S3 storage" do | ||
| 109 | setup do | ||
| 110 | rebuild_model :storage => :s3, | ||
| 111 | :bucket => "testing", | ||
| 112 | :path => ":attachment/:style/:basename.:extension", | ||
| 113 | :s3_credentials => { | ||
| 114 | 'access_key_id' => "12345", | ||
| 115 | 'secret_access_key' => "54321" | ||
| 116 | } | ||
| 117 | end | ||
| 118 | |||
| 119 | should "be extended by the S3 module" do | ||
| 120 | assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3) | ||
| 121 | end | ||
| 122 | |||
| 123 | should "not be extended by the Filesystem module" do | ||
| 124 | assert ! Dummy.new.avatar.is_a?(Paperclip::Storage::Filesystem) | ||
| 125 | end | ||
| 126 | |||
| 127 | context "when assigned" do | ||
| 128 | setup do | ||
| 129 | @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb') | ||
| 130 | @dummy = Dummy.new | ||
| 131 | @dummy.avatar = @file | ||
| 132 | end | ||
| 133 | |||
| 134 | teardown { @file.close } | ||
| 135 | |||
| 136 | should "not get a bucket to get a URL" do | ||
| 137 | @dummy.avatar.expects(:s3).never | ||
| 138 | @dummy.avatar.expects(:s3_bucket).never | ||
| 139 | assert_match %r{^http://s3\.amazonaws\.com/testing/avatars/original/5k\.png}, @dummy.avatar.url | ||
| 140 | end | ||
| 141 | |||
| 142 | context "and saved" do | ||
| 143 | setup do | ||
| 144 | @s3_mock = stub | ||
| 145 | @bucket_mock = stub | ||
| 146 | RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock) | ||
| 147 | @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock) | ||
| 148 | @key_mock = stub | ||
| 149 | @bucket_mock.expects(:key).returns(@key_mock) | ||
| 150 | @key_mock.expects(:data=) | ||
| 151 | @key_mock.expects(:put).with(nil, 'public-read', 'Content-type' => 'image/png') | ||
| 152 | @dummy.save | ||
| 153 | end | ||
| 154 | |||
| 155 | should "succeed" do | ||
| 156 | assert true | ||
| 157 | end | ||
| 158 | end | ||
| 159 | |||
| 160 | context "and remove" do | ||
| 161 | setup do | ||
| 162 | @s3_mock = stub | ||
| 163 | @bucket_mock = stub | ||
| 164 | RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock) | ||
| 165 | @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock) | ||
| 166 | @key_mock = stub | ||
| 167 | @bucket_mock.expects(:key).at_least(2).returns(@key_mock) | ||
| 168 | @key_mock.expects(:delete) | ||
| 169 | @dummy.destroy_attached_files | ||
| 170 | end | ||
| 171 | |||
| 172 | should "succeed" do | ||
| 173 | assert true | ||
| 174 | end | ||
| 175 | end | ||
| 176 | end | ||
| 177 | end | ||
| 178 | |||
| 179 | context "An attachment with S3 storage and bucket defined as a Proc" do | ||
| 180 | setup do | ||
| 181 | rebuild_model :storage => :s3, | ||
| 182 | :bucket => lambda { |attachment| "bucket_#{attachment.instance.other}" }, | ||
| 183 | :s3_credentials => {:not => :important} | ||
| 184 | end | ||
| 185 | |||
| 186 | should "get the right bucket name" do | ||
| 187 | assert "bucket_a", Dummy.new(:other => 'a').avatar.bucket_name | ||
| 188 | assert "bucket_b", Dummy.new(:other => 'b').avatar.bucket_name | ||
| 189 | end | ||
| 190 | end | ||
| 191 | |||
| 192 | context "An attachment with S3 storage and specific s3 headers set" do | ||
| 193 | setup do | ||
| 194 | rebuild_model :storage => :s3, | ||
| 195 | :bucket => "testing", | ||
| 196 | :path => ":attachment/:style/:basename.:extension", | ||
| 197 | :s3_credentials => { | ||
| 198 | 'access_key_id' => "12345", | ||
| 199 | 'secret_access_key' => "54321" | ||
| 200 | }, | ||
| 201 | :s3_headers => {'Cache-Control' => 'max-age=31557600'} | ||
| 202 | end | ||
| 203 | |||
| 204 | context "when assigned" do | ||
| 205 | setup do | ||
| 206 | @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb') | ||
| 207 | @dummy = Dummy.new | ||
| 208 | @dummy.avatar = @file | ||
| 209 | end | ||
| 210 | |||
| 211 | teardown { @file.close } | ||
| 212 | |||
| 213 | context "and saved" do | ||
| 214 | setup do | ||
| 215 | @s3_mock = stub | ||
| 216 | @bucket_mock = stub | ||
| 217 | RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock) | ||
| 218 | @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock) | ||
| 219 | @key_mock = stub | ||
| 220 | @bucket_mock.expects(:key).returns(@key_mock) | ||
| 221 | @key_mock.expects(:data=) | ||
| 222 | @key_mock.expects(:put).with(nil, | ||
| 223 | 'public-read', | ||
| 224 | 'Content-type' => 'image/png', | ||
| 225 | 'Cache-Control' => 'max-age=31557600') | ||
| 226 | @dummy.save | ||
| 227 | end | ||
| 228 | |||
| 229 | should "succeed" do | ||
| 230 | assert true | ||
| 231 | end | ||
| 232 | end | ||
| 233 | end | ||
| 234 | end | ||
| 235 | |||
| 236 | unless ENV["S3_TEST_BUCKET"].blank? | ||
| 237 | context "Using S3 for real, an attachment with S3 storage" do | ||
| 238 | setup do | ||
| 239 | rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" }, | ||
| 240 | :storage => :s3, | ||
| 241 | :bucket => ENV["S3_TEST_BUCKET"], | ||
| 242 | :path => ":class/:attachment/:id/:style.:extension", | ||
| 243 | :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")) | ||
| 244 | |||
| 245 | Dummy.delete_all | ||
| 246 | @dummy = Dummy.new | ||
| 247 | end | ||
| 248 | |||
| 249 | should "be extended by the S3 module" do | ||
| 250 | assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3) | ||
| 251 | end | ||
| 252 | |||
| 253 | context "when assigned" do | ||
| 254 | setup do | ||
| 255 | @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb') | ||
| 256 | @dummy.avatar = @file | ||
| 257 | end | ||
| 258 | |||
| 259 | teardown { @file.close } | ||
| 260 | |||
| 261 | should "still return a Tempfile when sent #to_io" do | ||
| 262 | assert_equal Tempfile, @dummy.avatar.to_io.class | ||
| 263 | end | ||
| 264 | |||
| 265 | context "and saved" do | ||
| 266 | setup do | ||
| 267 | @dummy.save | ||
| 268 | end | ||
| 269 | |||
| 270 | should "be on S3" do | ||
| 271 | assert true | ||
| 272 | end | ||
| 273 | end | ||
| 274 | end | ||
| 275 | end | ||
| 276 | end | ||
| 277 | end | ||
diff --git a/vendor/plugins/paperclip/test/thumbnail_test.rb b/vendor/plugins/paperclip/test/thumbnail_test.rb new file mode 100644 index 0000000..624e7fa --- /dev/null +++ b/vendor/plugins/paperclip/test/thumbnail_test.rb | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | require 'test/helper' | ||
| 2 | |||
| 3 | class ThumbnailTest < Test::Unit::TestCase | ||
| 4 | |||
| 5 | context "A Paperclip Tempfile" do | ||
| 6 | setup do | ||
| 7 | @tempfile = Paperclip::Tempfile.new("file.jpg") | ||
| 8 | end | ||
| 9 | |||
| 10 | should "have its path contain a real extension" do | ||
| 11 | assert_equal ".jpg", File.extname(@tempfile.path) | ||
| 12 | end | ||
| 13 | |||
| 14 | should "be a real Tempfile" do | ||
| 15 | assert @tempfile.is_a?(::Tempfile) | ||
| 16 | end | ||
| 17 | end | ||
| 18 | |||
| 19 | context "Another Paperclip Tempfile" do | ||
| 20 | setup do | ||
| 21 | @tempfile = Paperclip::Tempfile.new("file") | ||
| 22 | end | ||
| 23 | |||
| 24 | should "not have an extension if not given one" do | ||
| 25 | assert_equal "", File.extname(@tempfile.path) | ||
| 26 | end | ||
| 27 | |||
| 28 | should "still be a real Tempfile" do | ||
| 29 | assert @tempfile.is_a?(::Tempfile) | ||
| 30 | end | ||
| 31 | end | ||
| 32 | |||
| 33 | context "An image" do | ||
| 34 | setup do | ||
| 35 | @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb') | ||
| 36 | end | ||
| 37 | |||
| 38 | teardown { @file.close } | ||
| 39 | |||
| 40 | [["600x600>", "434x66"], | ||
| 41 | ["400x400>", "400x61"], | ||
| 42 | ["32x32<", "434x66"] | ||
| 43 | ].each do |args| | ||
| 44 | context "being thumbnailed with a geometry of #{args[0]}" do | ||
| 45 | setup do | ||
| 46 | @thumb = Paperclip::Thumbnail.new(@file, :geometry => args[0]) | ||
| 47 | end | ||
| 48 | |||
| 49 | should "start with dimensions of 434x66" do | ||
| 50 | cmd = %Q[identify -format "%wx%h" "#{@file.path}"] | ||
| 51 | assert_equal "434x66", `#{cmd}`.chomp | ||
| 52 | end | ||
| 53 | |||
| 54 | should "report the correct target geometry" do | ||
| 55 | assert_equal args[0], @thumb.target_geometry.to_s | ||
| 56 | end | ||
| 57 | |||
| 58 | context "when made" do | ||
| 59 | setup do | ||
| 60 | @thumb_result = @thumb.make | ||
| 61 | end | ||
| 62 | |||
| 63 | should "be the size we expect it to be" do | ||
| 64 | cmd = %Q[identify -format "%wx%h" "#{@thumb_result.path}"] | ||
| 65 | assert_equal args[1], `#{cmd}`.chomp | ||
| 66 | end | ||
| 67 | end | ||
| 68 | end | ||
| 69 | end | ||
| 70 | |||
| 71 | context "being thumbnailed at 100x50 with cropping" do | ||
| 72 | setup do | ||
| 73 | @thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x50#") | ||
| 74 | end | ||
| 75 | |||
| 76 | should "report its correct current and target geometries" do | ||
| 77 | assert_equal "100x50#", @thumb.target_geometry.to_s | ||
| 78 | assert_equal "434x66", @thumb.current_geometry.to_s | ||
| 79 | end | ||
| 80 | |||
| 81 | should "report its correct format" do | ||
| 82 | assert_nil @thumb.format | ||
| 83 | end | ||
| 84 | |||
| 85 | should "have whiny turned on by default" do | ||
| 86 | assert @thumb.whiny | ||
| 87 | end | ||
| 88 | |||
| 89 | should "have convert_options set to nil by default" do | ||
| 90 | assert_equal nil, @thumb.convert_options | ||
| 91 | end | ||
| 92 | |||
| 93 | should "send the right command to convert when sent #make" do | ||
| 94 | Paperclip.expects(:"`").with do |arg| | ||
| 95 | arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+\"x50\"\s+-crop\s+\"100x50\+114\+0\"\s+\+repage\s+".*?"} | ||
| 96 | end | ||
| 97 | @thumb.make | ||
| 98 | end | ||
| 99 | |||
| 100 | should "create the thumbnail when sent #make" do | ||
| 101 | dst = @thumb.make | ||
| 102 | assert_match /100x50/, `identify "#{dst.path}"` | ||
| 103 | end | ||
| 104 | end | ||
| 105 | |||
| 106 | context "being thumbnailed with convert options set" do | ||
| 107 | setup do | ||
| 108 | @thumb = Paperclip::Thumbnail.new(@file, | ||
| 109 | :geometry => "100x50#", | ||
| 110 | :convert_options => "-strip -depth 8") | ||
| 111 | end | ||
| 112 | |||
| 113 | should "have convert_options value set" do | ||
| 114 | assert_equal "-strip -depth 8", @thumb.convert_options | ||
| 115 | end | ||
| 116 | |||
| 117 | should "send the right command to convert when sent #make" do | ||
| 118 | Paperclip.expects(:"`").with do |arg| | ||
| 119 | arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+"x50"\s+-crop\s+"100x50\+114\+0"\s+\+repage\s+-strip\s+-depth\s+8\s+".*?"} | ||
| 120 | end | ||
| 121 | @thumb.make | ||
| 122 | end | ||
| 123 | |||
| 124 | should "create the thumbnail when sent #make" do | ||
| 125 | dst = @thumb.make | ||
| 126 | assert_match /100x50/, `identify "#{dst.path}"` | ||
| 127 | end | ||
| 128 | |||
| 129 | context "redefined to have bad convert_options setting" do | ||
| 130 | setup do | ||
| 131 | @thumb = Paperclip::Thumbnail.new(@file, | ||
| 132 | :geometry => "100x50#", | ||
| 133 | :convert_options => "-this-aint-no-option") | ||
| 134 | end | ||
| 135 | |||
| 136 | should "error when trying to create the thumbnail" do | ||
| 137 | assert_raises(Paperclip::PaperclipError) do | ||
| 138 | @thumb.make | ||
| 139 | end | ||
| 140 | end | ||
| 141 | end | ||
| 142 | end | ||
| 143 | end | ||
| 144 | |||
| 145 | context "A multipage PDF" do | ||
| 146 | setup do | ||
| 147 | @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "twopage.pdf"), 'rb') | ||
| 148 | end | ||
| 149 | |||
| 150 | teardown { @file.close } | ||
| 151 | |||
| 152 | should "start with two pages with dimensions 612x792" do | ||
| 153 | cmd = %Q[identify -format "%wx%h" "#{@file.path}"] | ||
| 154 | assert_equal "612x792"*2, `#{cmd}`.chomp | ||
| 155 | end | ||
| 156 | |||
| 157 | context "being thumbnailed at 100x100 with cropping" do | ||
| 158 | setup do | ||
| 159 | @thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x100#", :format => :png) | ||
| 160 | end | ||
| 161 | |||
| 162 | should "report its correct current and target geometries" do | ||
| 163 | assert_equal "100x100#", @thumb.target_geometry.to_s | ||
| 164 | assert_equal "612x792", @thumb.current_geometry.to_s | ||
| 165 | end | ||
| 166 | |||
| 167 | should "report its correct format" do | ||
| 168 | assert_equal :png, @thumb.format | ||
| 169 | end | ||
| 170 | |||
| 171 | should "create the thumbnail when sent #make" do | ||
| 172 | dst = @thumb.make | ||
| 173 | assert_match /100x100/, `identify "#{dst.path}"` | ||
| 174 | end | ||
| 175 | end | ||
| 176 | end | ||
| 177 | end | ||
