Permalink
Browse files

Merge remote branch 'upstream/master'

Conflicts:
	lib/paperclip/upfile.rb
	paperclip.gemspec
  • Loading branch information...
2 parents 3eb7b90 + cbfaca4 commit 237597eb3a920ebef8ad7d2ab0fb5e4e0ccd4d24 @monde monde committed Aug 24, 2011
Showing with 4,299 additions and 1,234 deletions.
  1. +6 −0 .gitignore
  2. +11 −0 .travis.yml
  3. +11 −0 Appraisals
  4. +38 −0 CONTRIBUTING.md
  5. +21 −0 Gemfile
  6. +100 −0 Gemfile.lock
  7. +270 −0 README.md
  8. +0 −174 README.rdoc
  9. +18 −41 Rakefile
  10. +17 −0 features/basic.feature
  11. +27 −0 features/s3.feature
  12. +14 −0 features/step_definitions/html_steps.rb
  13. +90 −0 features/step_definitions/rails_steps.rb
  14. +9 −0 features/step_definitions/s3_steps.rb
  15. +227 −0 features/step_definitions/web_steps.rb
  16. +3 −0 features/support/env.rb
  17. +35 −0 features/support/paths.rb
  18. +5 −0 features/support/rails.rb
  19. +25 −0 features/support/s3.rb
  20. +20 −0 gemfiles/rails2.gemfile
  21. +121 −0 gemfiles/rails2.gemfile.lock
  22. +20 −0 gemfiles/rails3.gemfile
  23. +163 −0 gemfiles/rails3.gemfile.lock
  24. +20 −0 gemfiles/rails3_1.gemfile
  25. +172 −0 gemfiles/rails3_1.gemfile.lock
  26. +2 −2 generators/paperclip/USAGE
  27. +8 −8 generators/paperclip/paperclip_generator.rb
  28. +3 −0 init.rb
  29. +8 −0 lib/generators/paperclip/USAGE
  30. +33 −0 lib/generators/paperclip/paperclip_generator.rb
  31. +19 −0 lib/generators/paperclip/templates/paperclip_migration.rb.erb
  32. +156 −97 lib/paperclip.rb
  33. +140 −72 lib/paperclip/attachment.rb
  34. +0 −33 lib/paperclip/callback_compatability.rb
  35. +61 −0 lib/paperclip/callback_compatibility.rb
  36. +11 −8 lib/paperclip/geometry.rb
  37. +67 −11 lib/paperclip/interpolations.rb
  38. +12 −26 lib/paperclip/iostream.rb
  39. +29 −0 lib/paperclip/matchers.rb
  40. +8 −0 lib/paperclip/matchers/have_attached_file_matcher.rb
  41. +30 −14 lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
  42. +9 −3 lib/paperclip/matchers/validate_attachment_presence_matcher.rb
  43. +12 −2 lib/paperclip/matchers/validate_attachment_size_matcher.rb
  44. +15 −6 lib/paperclip/processor.rb
  45. +24 −0 lib/paperclip/railtie.rb
  46. +3 −247 lib/paperclip/storage.rb
  47. +78 −0 lib/paperclip/storage/filesystem.rb
  48. +133 −0 lib/paperclip/storage/fog.rb
  49. +230 −0 lib/paperclip/storage/s3.rb
  50. +19 −14 lib/paperclip/style.rb
  51. +38 −19 lib/paperclip/thumbnail.rb
  52. +24 −12 lib/paperclip/upfile.rb
  53. +3 −0 lib/paperclip/version.rb
  54. +81 −0 lib/tasks/paperclip.rake
  55. +33 −45 paperclip.gemspec
  56. +2 −0 rails/init.rb
  57. +5 −4 shoulda_macros/paperclip.rb
  58. +0 −79 tasks/paperclip_tasks.rake
  59. +346 −45 test/attachment_test.rb
  60. BIN test/fixtures/animated.gif
  61. BIN test/fixtures/uppercase.PNG
  62. +131 −0 test/fog_test.rb
  63. +31 −2 test/geometry_test.rb
  64. +71 −30 test/helper.rb
  65. +166 −11 test/integration_test.rb
  66. +74 −10 test/interpolations_test.rb
  67. +7 −14 test/iostream_test.rb
  68. +10 −7 test/matchers/have_attached_file_matcher_test.rb
  69. +65 −9 test/matchers/validate_attachment_content_type_matcher_test.rb
  70. +9 −6 test/matchers/validate_attachment_presence_matcher_test.rb
  71. +18 −18 test/matchers/validate_attachment_size_matcher_test.rb
  72. +76 −95 test/paperclip_test.rb
  73. +1 −1 test/processor_test.rb
  74. +347 −24 test/storage_test.rb
  75. +66 −21 test/style_test.rb
  76. +130 −21 test/thumbnail_test.rb
  77. +12 −3 test/upfile_test.rb
View
6 .gitignore
@@ -3,3 +3,9 @@
tmp
test/s3.yml
public
+paperclip*.gem
+capybara*.html
+*.rbc
+.bundle
+*SPIKE*
+.rvmrc
View
11 .travis.yml
@@ -0,0 +1,11 @@
+rvm:
+ - 1.8.7
+ - 1.9.2
+ - ree
+ - rbx-2.0
+
+script: "bundle exec rake clean test"
+gemfile:
+ - gemfiles/rails2.gemfile
+ - gemfiles/rails3.gemfile
+ - gemfiles/rails3_1.gemfile
View
11 Appraisals
@@ -0,0 +1,11 @@
+appraise "rails2" do
+ gem "rails", "~> 2.3.12"
+end
+
+appraise "rails3" do
+ gem "rails", "~> 3.0.9"
+end
+
+appraise "rails3_1" do
+ gem "rails", "~> 3.1.0.rc5"
+end
View
38 CONTRIBUTING.md
@@ -0,0 +1,38 @@
+We love pull requests. Here's a quick guide:
+
+1. Fork the repo.
+
+2. Run the tests. We only take pull requests with passing tests, and it's great
+to know that you have a clean slate: `bundle && rake`
+
+3. Add a test for your change. Only refactoring and documentation changes
+require no new tests. If you are adding functionality or fixing a bug, we need
+a test!
+
+4. Make the test pass.
+
+5. Push to your fork and submit a pull request.
+
+
+At this point you're waiting on us. We like to at least comment on, if not
+accept, pull requests within three business days (and, typically, one business
+day). We may suggest some changes or improvements or alternatives.
+
+Some things that will increase the chance that your pull request is accepted,
+taken straight from the Ruby on Rails guide:
+
+* Use Rails idioms and helpers
+* Include tests that fail without your code, and pass with it
+* Update the documentation, the surrounding one, examples elsewhere, guides,
+ whatever is affected by your contribution
+
+Syntax:
+
+* Two spaces, no tabs.
+* No trailing whitespace. Blank lines should not have any space.
+* Prefer &&/|| over and/or.
+* MyClass.my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
+* a = b and not a=b.
+* Follow the conventions you see used in the source already.
+
+And in case we didn't emphasize it enough: we love tests!
View
21 Gemfile
@@ -0,0 +1,21 @@
+source "http://rubygems.org"
+
+gem "activerecord", :require => "active_record"
+gem "appraisal"
+gem "aws-s3", :require => "aws/s3"
+gem "bundler"
+gem "cocaine", "~>0.2"
+gem "fog"
+gem "jruby-openssl", :platform => :jruby
+gem "mime-types"
+gem "mocha"
+gem "rake"
+gem "rdoc", :require => false
+gem "shoulda"
+gem "sqlite3", "~>1.3.4"
+
+# This is for Rails 3.1
+gem "sprockets", "~> 2.0.0.beta.13", :require => false
+
+# gem "ruby-debug", :platform => :ruby_18
+# gem "ruby-debug19", :platform => :ruby_19
View
100 Gemfile.lock
@@ -0,0 +1,100 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activerecord (2.3.12)
+ activesupport (= 2.3.12)
+ activesupport (2.3.12)
+ appraisal (0.3.5)
+ aruba (~> 0.3.6)
+ bundler
+ rake
+ aruba (0.3.7)
+ childprocess (>= 0.1.9)
+ cucumber (>= 0.10.5)
+ rspec (>= 2.6.0)
+ aws-s3 (0.6.2)
+ builder
+ mime-types
+ xml-simple
+ bouncy-castle-java (1.5.0146.1)
+ builder (3.0.0)
+ childprocess (0.1.9)
+ ffi (~> 1.0.6)
+ cocaine (0.2.0)
+ cucumber (0.10.5)
+ builder (>= 2.1.2)
+ diff-lcs (>= 1.1.2)
+ gherkin (~> 2.4.0)
+ json (>= 1.4.6)
+ term-ansicolor (>= 1.0.5)
+ diff-lcs (1.1.2)
+ excon (0.6.3)
+ ffi (1.0.9)
+ ffi (1.0.9-java)
+ fog (0.8.2)
+ builder
+ excon (~> 0.6.1)
+ formatador (>= 0.1.3)
+ json
+ mime-types
+ net-ssh (>= 2.1.3)
+ nokogiri (>= 1.4.4)
+ ruby-hmac
+ formatador (0.1.4)
+ gherkin (2.4.5)
+ json (>= 1.4.6)
+ gherkin (2.4.5-java)
+ json (>= 1.4.6)
+ hike (1.2.0)
+ jruby-openssl (0.7.4)
+ bouncy-castle-java
+ json (1.5.3)
+ json (1.5.3-java)
+ mime-types (1.16)
+ mocha (0.9.12)
+ net-ssh (2.1.4)
+ nokogiri (1.4.4)
+ nokogiri (1.4.4-java)
+ weakling (>= 0.0.3)
+ rack (1.3.2)
+ rake (0.9.2)
+ rdoc (3.8)
+ rspec (2.6.0)
+ rspec-core (~> 2.6.0)
+ rspec-expectations (~> 2.6.0)
+ rspec-mocks (~> 2.6.0)
+ rspec-core (2.6.4)
+ rspec-expectations (2.6.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.6.0)
+ ruby-hmac (0.4.0)
+ shoulda (2.11.3)
+ sprockets (2.0.0.beta.13)
+ hike (~> 1.2)
+ rack (~> 1.0)
+ tilt (~> 1.1, != 1.3.0)
+ sqlite3 (1.3.4)
+ term-ansicolor (1.0.5)
+ tilt (1.3.2)
+ weakling (0.0.4-java)
+ xml-simple (1.0.16)
+
+PLATFORMS
+ java
+ ruby
+
+DEPENDENCIES
+ activerecord
+ appraisal
+ aws-s3
+ bundler
+ cocaine (~> 0.2)
+ fog
+ jruby-openssl
+ mime-types
+ mocha
+ rake
+ rdoc
+ shoulda
+ sprockets (~> 2.0.0.beta.13)
+ sqlite3 (~> 1.3.4)
View
270 README.md
@@ -0,0 +1,270 @@
+# Paperclip [![Build Status](https://secure.travis-ci.org/thoughtbot/paperclip.png?branch=master)](http://travis-ci.org/thoughtbot/paperclip)
+
+Paperclip is intended as an easy file attachment library for ActiveRecord. The
+intent behind it was to keep setup as easy as possible and to treat files as
+much like other attributes as possible. This means they aren't saved to their
+final locations on disk, nor are they deleted if set to nil, until
+ActiveRecord::Base#save is called. It manages validations based on size and
+presence, if required. It can transform its assigned image into thumbnails if
+needed, and the prerequisites are as simple as installing ImageMagick (which,
+for most modern Unix-based systems, is as easy as installing the right
+packages). Attached files are saved to the filesystem and referenced in the
+browser by an easily understandable specification, which has sensible and
+useful defaults.
+
+See the documentation for `has_attached_file` in Paperclip::ClassMethods for
+more detailed options.
+
+The complete [RDoc](http://rdoc.info/gems/paperclip) is online.
+
+Requirements
+------------
+
+ImageMagick must be installed and Paperclip must have access to it. To ensure
+that it does, on your command line, run `which convert` (one of the ImageMagick
+utilities). This will give you the path where that utility is installed. For
+example, it might return `/usr/local/bin/convert`.
+
+Then, in your environment config file, let Paperclip know to look there by adding that
+directory to its path.
+
+In development mode, you might add this line to `config/environments/development.rb)`:
+
+ Paperclip.options[:command_path] = "/usr/local/bin/"
+
+If you're on Mac OSX, you'll want to run the following with Homebrew:
+
+ brew install imagemagick
+
+If you are dealing with pdf uploads or running the test suite, also run:
+
+ brew install gs
+
+Installation
+------------
+
+Paperclip is distributed as a gem, which is how it should be used in your app. It's
+technically still installable as a plugin, but that's discouraged, as Rails plays
+well with gems.
+
+Include the gem in your Gemfile:
+
+ gem "paperclip", "~> 2.3"
+
+Or, if you don't use Bundler (though you probably should, even in Rails 2), with config.gem
+
+ # In config/environment.rb
+ ...
+ Rails::Initializer.run do |config|
+ ...
+ config.gem "paperclip", :version => "~> 2.3"
+ ...
+ end
+
+Quick Start
+-----------
+
+In your model:
+
+ class User < ActiveRecord::Base
+ has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
+ end
+
+In your migrations:
+
+ class AddAvatarColumnsToUser < ActiveRecord::Migration
+ def self.up
+ add_column :users, :avatar_file_name, :string
+ add_column :users, :avatar_content_type, :string
+ add_column :users, :avatar_file_size, :integer
+ add_column :users, :avatar_updated_at, :datetime
+ end
+
+ def self.down
+ remove_column :users, :avatar_file_name
+ remove_column :users, :avatar_content_type
+ remove_column :users, :avatar_file_size
+ remove_column :users, :avatar_updated_at
+ end
+ end
+
+In your edit and new views:
+
+ <% form_for :user, @user, :url => user_path, :html => { :multipart => true } do |form| %>
+ <%= form.file_field :avatar %>
+ <% end %>
+
+In your controller:
+
+ def create
+ @user = User.create( params[:user] )
+ end
+
+In your show view:
+
+ <%= image_tag @user.avatar.url %>
+ <%= image_tag @user.avatar.url(:medium) %>
+ <%= image_tag @user.avatar.url(:thumb) %>
+
+To detach a file, simply set the attribute to `nil`:
+
+ @user.avatar = nil
+ @user.save
+
+Usage
+-----
+
+The basics of paperclip are quite simple: Declare that your model has an
+attachment with the has_attached_file method, and give it a name. Paperclip
+will wrap up up to four attributes (all prefixed with that attachment's name,
+so you can have multiple attachments per model if you wish) and give them a
+friendly front end. The attributes are `<attachment>_file_name`,
+`<attachment>_file_size`, `<attachment>_content_type`, and `<attachment>_updated_at`.
+Only `<attachment>_file_name` is required for paperclip to operate. More
+information about the options to has_attached_file is available in the
+documentation of Paperclip::ClassMethods.
+
+Attachments can be validated with Paperclip's validation methods,
+validates_attachment_presence, validates_attachment_content_type, and
+validates_attachment_size.
+
+Storage
+-------
+
+The files that are assigned as attachments are, by default, placed in the
+directory specified by the :path option to has_attached_file. By default, this
+location is ":rails_root/public/system/:attachment/:id/:style/:filename". This
+location was chosen because on standard Capistrano deployments, the
+public/system directory is symlinked to the app's shared directory, meaning it
+will survive between deployments. For example, using that :path, you may have a
+file at
+
+ /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png
+
+_NOTE: This is a change from previous versions of Paperclip, but is overall a
+safer choice for the default file store._
+
+You may also choose to store your files using Amazon's S3 service. You can find
+more information about S3 storage at the description for
+Paperclip::Storage::S3.
+
+Files on the local filesystem (and in the Rails app's public directory) will be
+available to the internet at large. If you require access control, it's
+possible to place your files in a different location. You will need to change
+both the :path and :url options in order to make sure the files are unavailable
+to the public. Both :path and :url allow the same set of interpolated
+variables.
+
+Post Processing
+---------------
+
+Paperclip supports an extensible selection of post-processors. When you define
+a set of styles for an attachment, by default it is expected that those
+"styles" are actually "thumbnails". However, you can do much more than just
+thumbnail images. By defining a subclass of Paperclip::Processor, you can
+perform any processing you want on the files that are attached. Any file in
+your Rails app's lib/paperclip_processors directory is automatically loaded by
+paperclip, allowing you to easily define custom processors. You can specify a
+processor with the :processors option to has_attached_file:
+
+ has_attached_file :scan, :styles => { :text => { :quality => :better } },
+ :processors => [:ocr]
+
+This would load the hypothetical class Paperclip::Ocr, which would have the
+hash "{ :quality => :better }" passed to it along with the uploaded file. For
+more information about defining processors, see Paperclip::Processor.
+
+The default processor is Paperclip::Thumbnail. For backwards compatability
+reasons, you can pass a single geometry string or an array containing a
+geometry and a format, which the file will be converted to, like so:
+
+ has_attached_file :avatar, :styles => { :thumb => ["32x32#", :png] }
+
+This will convert the "thumb" style to a 32x32 square in png format, regardless
+of what was uploaded. If the format is not specified, it is kept the same (i.e.
+jpgs will remain jpgs).
+
+Multiple processors can be specified, and they will be invoked in the order
+they are defined in the :processors array. Each successive processor will
+be given the result of the previous processor's execution. All processors will
+receive the same parameters, which are what you define in the :styles hash.
+For example, assuming we had this definition:
+
+ has_attached_file :scan, :styles => { :text => { :quality => :better } },
+ :processors => [:rotator, :ocr]
+
+then both the :rotator processor and the :ocr processor would receive the
+options "{ :quality => :better }". This parameter may not mean anything to one
+or more or the processors, and they are expected to ignore it.
+
+_NOTE: Because processors operate by turning the original attachment into the
+styles, no processors will be run if there are no styles defined._
+
+If you're interested in caching your thumbnail's width, height and size in the
+database, take a look at the [paperclip-meta](https://github.com/y8/paperclip-meta) gem.
+
+Also, if you're interesting to generate the thumbnail on-the-fly, you might want
+to look into the [attachment_on_the_fly](https://github.com/drpentode/Attachment-on-the-Fly) gem.
+
+Events
+------
+
+Before and after the Post Processing step, Paperclip calls back to the model
+with a few callbacks, allowing the model to change or cancel the processing
+step. The callbacks are `before_post_process` and `after_post_process` (which
+are called before and after the processing of each attachment), and the
+attachment-specific `before_<attachment>_post_process` and
+`after_<attachment>_post_process`. The callbacks are intended to be as close to
+normal ActiveRecord callbacks as possible, so if you return false (specifically
+- returning nil is not the same) in a before_ filter, the post processing step
+will halt. Returning false in an after_ filter will not halt anything, but you
+can access the model and the attachment if necessary.
+
+_NOTE: Post processing will not even *start* if the attachment is not valid
+according to the validations. Your callbacks and processors will *only* be
+called with valid attachments._
+
+URI Obfuscation
+---------------
+
+Paperclip has an interpolation called `:hash` for obfuscating filenames of publicly-available files. For more on this feature read author's own explanation.
+
+[https://github.com/thoughtbot/paperclip/pull/416](https://github.com/thoughtbot/paperclip/pull/416)
+
+Testing
+-------
+
+Paperclip provides rspec-compatible matchers for testing attachments. See the
+documentation on [Paperclip::Shoulda::Matchers](http://rubydoc.info/gems/paperclip/Paperclip/Shoulda/Matchers)
+for more information.
+
+Contributing
+------------
+
+If you'd like to contribute a feature or bugfix: Thanks! To make sure your
+fix/feature has a high chance of being included, please read the following
+guidelines:
+
+1. Ask on the mailing list[http://groups.google.com/group/paperclip-plugin], or
+ post a new GitHub Issue[http://github.com/thoughtbot/paperclip/issues].
+2. Make sure there are tests! We will not accept any patch that is not tested.
+ It's a rare time when explicit tests aren't needed. If you have questions
+ about writing tests for paperclip, please ask the mailing list.
+
+Please see CONTRIBUTING.md for details.
+
+Credits
+-------
+
+![thoughtbot](http://thoughtbot.com/images/tm/logo.png)
+
+Paperclip is maintained and funded by [thoughtbot, inc](http://thoughtbot.com/community)
+
+Thank you to all [the contributors](https://github.com/thoughtbot/paperclip/contributors)!
+
+The names and logos for thoughtbot are trademarks of thoughtbot, inc.
+
+License
+-------
+
+Paperclip is Copyright © 2008-2011 thoughtbot. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
View
174 README.rdoc
@@ -1,174 +0,0 @@
-=Paperclip
-
-Paperclip is intended as an easy file attachment library for ActiveRecord. The
-intent behind it was to keep setup as easy as possible and to treat files as
-much like other attributes as possible. This means they aren't saved to their
-final locations on disk, nor are they deleted if set to nil, until
-ActiveRecord::Base#save is called. It manages validations based on size and
-presence, if required. It can transform its assigned image into thumbnails if
-needed, and the prerequisites are as simple as installing ImageMagick (which,
-for most modern Unix-based systems, is as easy as installing the right
-packages). Attached files are saved to the filesystem and referenced in the
-browser by an easily understandable specification, which has sensible and
-useful defaults.
-
-See the documentation for +has_attached_file+ in Paperclip::ClassMethods for
-more detailed options.
-
-==Quick Start
-
-In your model:
-
- class User < ActiveRecord::Base
- has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
- end
-
-In your migrations:
-
- class AddAvatarColumnsToUser < ActiveRecord::Migration
- def self.up
- add_column :users, :avatar_file_name, :string
- add_column :users, :avatar_content_type, :string
- add_column :users, :avatar_file_size, :integer
- add_column :users, :avatar_updated_at, :datetime
- end
-
- def self.down
- remove_column :users, :avatar_file_name
- remove_column :users, :avatar_content_type
- remove_column :users, :avatar_file_size
- remove_column :users, :avatar_updated_at
- end
- end
-
-In your edit and new views:
-
- <% form_for :user, @user, :url => user_path, :html => { :multipart => true } do |form| %>
- <%= form.file_field :avatar %>
- <% end %>
-
-In your controller:
-
- def create
- @user = User.create( params[:user] )
- end
-
-In your show view:
-
- <%= image_tag @user.avatar.url %>
- <%= image_tag @user.avatar.url(:medium) %>
- <%= image_tag @user.avatar.url(:thumb) %>
-
-==Usage
-
-The basics of paperclip are quite simple: Declare that your model has an
-attachment with the has_attached_file method, and give it a name. Paperclip
-will wrap up up to four attributes (all prefixed with that attachment's name,
-so you can have multiple attachments per model if you wish) and give the a
-friendly front end. The attributes are <attachment>_file_name,
-<attachment>_file_size, <attachment>_content_type, and <attachment>_updated_at.
-Only <attachment>_file_name is required for paperclip to operate. More
-information about the options to has_attached_file is available in the
-documentation of Paperclip::ClassMethods.
-
-Attachments can be validated with Paperclip's validation methods,
-validates_attachment_presence, validates_attachment_content_type, and
-validates_attachment_size.
-
-==Storage
-
-The files that are assigned as attachments are, by default, placed in the
-directory specified by the :path option to has_attached_file. By default, this
-location is ":rails_root/public/system/:attachment/:id/:style/:filename". This
-location was chosen because on standard Capistrano deployments, the
-public/system directory is symlinked to the app's shared directory, meaning it
-will survive between deployments. For example, using that :path, you may have a
-file at
-
- /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png
-
-NOTE: This is a change from previous versions of Paperclip, but is overall a
-safer choice for the default file store.
-
-You may also choose to store your files using Amazon's S3 service. You can find
-more information about S3 storage at the description for
-Paperclip::Storage::S3.
-
-Files on the local filesystem (and in the Rails app's public directory) will be
-available to the internet at large. If you require access control, it's
-possible to place your files in a different location. You will need to change
-both the :path and :url options in order to make sure the files are unavailable
-to the public. Both :path and :url allow the same set of interpolated
-variables.
-
-==Post Processing
-
-Paperclip supports an extensible selection of post-processors. When you define
-a set of styles for an attachment, by default it is expected that those
-"styles" are actually "thumbnails". However, you can do much more than just
-thumbnail images. By defining a subclass of Paperclip::Processor, you can
-perform any processing you want on the files that are attached. Any file in
-your Rails app's lib/paperclip_processors directory is automatically loaded by
-paperclip, allowing you to easily define custom processors. You can specify a
-processor with the :processors option to has_attached_file:
-
- has_attached_file :scan, :styles => { :text => { :quality => :better } },
- :processors => [:ocr]
-
-This would load the hypothetical class Paperclip::Ocr, which would have the
-hash "{ :quality => :better }" passed to it along with the uploaded file. For
-more information about defining processors, see Paperclip::Processor.
-
-The default processor is Paperclip::Thumbnail. For backwards compatability
-reasons, you can pass a single geometry string or an array containing a
-geometry and a format, which the file will be converted to, like so:
-
- has_attached_file :avatar, :styles => { :thumb => ["32x32#", :png] }
-
-This will convert the "thumb" style to a 32x32 square in png format, regardless
-of what was uploaded. If the format is not specified, it is kept the same (i.e.
-jpgs will remain jpgs).
-
-Multiple processors can be specified, and they will be invoked in the order
-they are defined in the :processors array. Each successive processor will
-be given the result of the previous processor's execution. All processors will
-receive the same parameters, which are what you define in the :styles hash.
-For example, assuming we had this definition:
-
- has_attached_file :scan, :styles => { :text => { :quality => :better } },
- :processors => [:rotator, :ocr]
-
-then both the :rotator processor and the :ocr processor would receive the
-options "{ :quality => :better }". This parameter may not mean anything to one
-or more or the processors, and they are expected to ignore it.
-
-NOTE: Because processors operate by turning the original attachment into the
-styles, no processors will be run if there are no styles defined.
-
-==Events
-
-Before and after the Post Processing step, Paperclip calls back to the model
-with a few callbacks, allowing the model to change or cancel the processing
-step. The callbacks are "before_post_process" and "after_post_process" (which
-are called before and after the processing of each attachment), and the
-attachment-specific "before_<attachment>_post_process" and
-"after_<attachment>_post_process". The callbacks are intended to be as close to
-normal ActiveRecord callbacks as possible, so if you return false (specifically
-- returning nil is not the same) in a before_ filter, the post processing step
-will halt. Returning false in an after_ filter will not halt anything, but you
-can access the model and the attachment if necessary.
-
-NOTE: Post processing will not even *start* if the attachment is not valid
-according to the validations. Your callbacks and processors will *only* be
-called with valid attachments.
-
-==Contributing
-
-If you'd like to contribute a feature or bugfix: Thanks! To make sure your
-fix/feature has a high chance of being included, please read the following
-guidelines:
-
-1. Ask on the mailing list, or post a new GitHub Issue.
-2. Make sure there are tests! We will not accept any patch that is not tested.
- It's a rare time when explicit tests aren't needed. If you have questions
- about writing tests for paperclip, please ask the mailing list.
View
59 Rakefile
@@ -1,12 +1,21 @@
+require 'rubygems'
+require 'appraisal'
+require 'bundler/setup'
+
require 'rake'
require 'rake/testtask'
-require 'rake/rdoctask'
+require 'rdoc/task'
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
require 'paperclip'
desc 'Default: run unit tests.'
-task :default => [:clean, :test]
+task :default => [:clean, 'appraisal:install', :all]
+
+desc 'Test the paperclip plugin under all supported Rails versions.'
+task :all do |t|
+ exec('rake appraisal test')
+end
desc 'Test the paperclip plugin.'
Rake::TestTask.new(:test) do |t|
@@ -22,7 +31,7 @@ task :shell do |t|
end
desc 'Generate documentation for the paperclip plugin.'
-Rake::RDocTask.new(:rdoc) do |rdoc|
+RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'doc'
rdoc.title = 'Paperclip'
rdoc.options << '--line-numbers' << '--inline-source'
@@ -40,47 +49,15 @@ task :clean do |t|
FileUtils.rm_rf "doc"
FileUtils.rm_rf "tmp"
FileUtils.rm_rf "pkg"
+ FileUtils.rm_rf "public"
FileUtils.rm "test/debug.log" rescue nil
FileUtils.rm "test/paperclip.db" rescue nil
Dir.glob("paperclip-*.gem").each{|f| FileUtils.rm f }
end
-include_file_globs = ["README*",
- "LICENSE",
- "Rakefile",
- "init.rb",
- "{generators,lib,tasks,test,shoulda_macros}/**/*"]
-exclude_file_globs = ["test/s3.yml",
- "test/debug.log",
- "test/paperclip.db",
- "test/doc",
- "test/doc/*",
- "test/pkg",
- "test/pkg/*",
- "test/tmp",
- "test/tmp/*"]
-spec = Gem::Specification.new do |s|
- s.name = "paperclip"
- s.version = Paperclip::VERSION
- s.author = "Jon Yurek"
- s.email = "jyurek@thoughtbot.com"
- s.homepage = "http://www.thoughtbot.com/projects/paperclip"
- s.platform = Gem::Platform::RUBY
- s.summary = "File attachments as attributes for ActiveRecord"
- s.files = FileList[include_file_globs].to_a - FileList[exclude_file_globs].to_a
- s.require_path = "lib"
- s.test_files = FileList["test/**/test_*.rb"].to_a
- s.rubyforge_project = "paperclip"
- s.has_rdoc = true
- s.extra_rdoc_files = FileList["README*"].to_a
- s.rdoc_options << '--line-numbers' << '--inline-source'
- s.requirements << "ImageMagick"
- s.add_development_dependency 'shoulda'
- s.add_development_dependency 'jferris-mocha', '>= 0.9.5.0.1241126838'
- s.add_development_dependency 'aws-s3'
- s.add_development_dependency 'sqlite3-ruby'
- s.add_development_dependency 'activerecord'
- s.add_development_dependency 'activesupport'
+desc 'Build the gemspec.'
+task :gemspec do |t|
+ exec 'gem build paperclip.gemspec'
end
desc "Print a list of the files to be put into the gem"
@@ -89,13 +66,13 @@ task :manifest => :clean do
puts file
end
end
-
+
desc "Generate a gemspec file for GitHub"
task :gemspec => :clean do
File.open("#{spec.name}.gemspec", 'w') do |f|
f.write spec.to_ruby
end
-end
+end
desc "Build the gem into the current directory"
task :gem => :gemspec do
View
17 features/basic.feature
@@ -0,0 +1,17 @@
+Feature: Running paperclip in a Rails app
+
+ Scenario: Basic utilization
+ Given I have a rails application
+ And I save the following as "app/models/user.rb"
+ """
+ class User < ActiveRecord::Base
+ has_attached_file :avatar
+ end
+ """
+ When I visit /users/new
+ And I fill in "user_name" with "something"
+ And I attach the file "test/fixtures/5k.png" to "user_avatar"
+ And I press "Submit"
+ Then I should see "Name: something"
+ And I should see an image with a path of "/system/avatars/1/original/5k.png"
+ And the file at "/system/avatars/1/original/5k.png" is the same as "test/fixtures/5k.png"
View
27 features/s3.feature
@@ -0,0 +1,27 @@
+Feature: Running paperclip in a Rails app using basic S3 support
+
+ Scenario: Basic utilization
+ Given I have a rails application
+ And I save the following as "app/models/user.rb"
+ """
+ class User < ActiveRecord::Base
+ has_attached_file :avatar,
+ :storage => :s3,
+ :path => "/:attachment/:id/:style/:filename",
+ :s3_credentials => Rails.root.join("config/s3.yml")
+ end
+ """
+ And I validate my S3 credentials
+ And I save the following as "config/s3.yml"
+ """
+ bucket: <%= ENV['PAPERCLIP_TEST_BUCKET'] || 'paperclip' %>
+ access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
+ secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
+ """
+ When I visit /users/new
+ And I fill in "user_name" with "something"
+ And I attach the file "test/fixtures/5k.png" to "user_avatar"
+ And I press "Submit"
+ Then I should see "Name: something"
+ And I should see an image with a path of "http://s3.amazonaws.com/paperclip/avatars/1/original/5k.png"
+ And the file at "http://s3.amazonaws.com/paperclip/avatars/1/original/5k.png" is the same as "test/fixtures/5k.png"
View
14 features/step_definitions/html_steps.rb
@@ -0,0 +1,14 @@
+Then %r{I should see an image with a path of "([^"]*)"} do |path|
+ page.should have_css("img[src^='#{path}']")
+end
+
+Then %r{^the file at "([^"]*)" is the same as "([^"]*)"$} do |web_file, path|
+ expected = IO.read(path)
+ actual = if web_file.match %r{^https?://}
+ Net::HTTP.get(URI.parse(web_file))
+ else
+ visit(web_file)
+ page.body
+ end
+ actual.should == expected
+end
View
90 features/step_definitions/rails_steps.rb
@@ -0,0 +1,90 @@
+Given "I have a rails application" do
+ steps %{
+ Given I generate a rails application
+ And this plugin is available
+ And I have a "users" resource with "name:string"
+ And I turn off class caching
+ Given I save the following as "app/models/user.rb"
+ """
+ class User < ActiveRecord::Base
+ end
+ """
+ And I save the following as "config/s3.yml"
+ """
+ access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
+ secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
+ bucket: paperclip
+ """
+ And I save the following as "app/views/users/new.html.erb"
+ """
+ <% form_for @user, :html => { :multipart => true } do |f| %>
+ <%= f.text_field :name %>
+ <%= f.file_field :avatar %>
+ <%= submit_tag "Submit" %>
+ <% end %>
+ """
+ And I save the following as "app/views/users/show.html.erb"
+ """
+ <p>Name: <%= @user.name %></p>
+ <p>Avatar: <%= image_tag @user.avatar.url %></p>
+ """
+ And I run "script/generate paperclip user avatar"
+ And the rails application is prepped and running
+ }
+end
+
+Given %r{I generate a rails application} do
+ FileUtils.rm_rf TEMP_ROOT
+ FileUtils.mkdir_p TEMP_ROOT
+ Dir.chdir(TEMP_ROOT) do
+ `rails _2.3.8_ #{APP_NAME}`
+ end
+end
+
+When %r{I save the following as "([^"]*)"} do |path, string|
+ FileUtils.mkdir_p(File.join(CUC_RAILS_ROOT, File.dirname(path)))
+ File.open(File.join(CUC_RAILS_ROOT, path), 'w') { |file| file.write(string) }
+end
+
+When %r{I turn off class caching} do
+ Dir.chdir(CUC_RAILS_ROOT) do
+ file = "config/environments/test.rb"
+ config = IO.read(file)
+ config.gsub!(%r{^\s*config.cache_classes.*$},
+ "config.cache_classes = false")
+ File.open(file, "w"){|f| f.write(config) }
+ end
+end
+
+When %r{the rails application is prepped and running$} do
+ When "I reset the database"
+ When "the rails application is running"
+end
+
+When %r{I reset the database} do
+ When %{I run "rake db:drop db:create db:migrate"}
+end
+
+When %r{the rails application is running} do
+ Dir.chdir(CUC_RAILS_ROOT) do
+ require "config/environment"
+ require "capybara/rails"
+ end
+end
+
+When %r{this plugin is available} do
+ $LOAD_PATH << "#{PROJECT_ROOT}/lib"
+ require 'paperclip'
+ When %{I save the following as "vendor/plugins/paperclip/rails/init.rb"},
+ IO.read("#{PROJECT_ROOT}/rails/init.rb")
+end
+
+When %r{I run "([^"]*)"} do |command|
+ Dir.chdir(CUC_RAILS_ROOT) do
+ `#{command}`
+ end
+end
+
+When %r{I have a "([^"]*)" resource with "([^"]*)"} do |resource, fields|
+ When %{I run "script/generate scaffold #{resource} #{fields}"}
+end
View
9 features/step_definitions/s3_steps.rb
@@ -0,0 +1,9 @@
+Given /I validate my S3 credentials/ do
+ key = ENV['AWS_ACCESS_KEY_ID']
+ secret = ENV['AWS_SECRET_ACCESS_KEY']
+
+ key.should_not be_nil
+ secret.should_not be_nil
+
+ assert_credentials(key, secret)
+end
View
227 features/step_definitions/web_steps.rb
@@ -0,0 +1,227 @@
+# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
+# It is recommended to regenerate this file in the future when you upgrade to a
+# newer version of cucumber-rails. Consider adding your own code to a new file
+# instead of editing this one. Cucumber will automatically load all features/**/*.rb
+# files.
+
+
+require 'uri'
+require 'cgi'
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
+
+module WithinHelpers
+ def with_scope(locator)
+ locator ? within(locator) { yield } : yield
+ end
+end
+World(WithinHelpers)
+
+Given /^(?:|I )am on (.+)$/ do |page_name|
+ visit path_to(page_name)
+end
+
+When /^(?:|I )go to (.+)$/ do |page_name|
+ visit path_to(page_name)
+end
+
+When /^(?:|I )visit (\/.+)$/ do |page_path|
+ visit page_path
+end
+
+When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector|
+ with_scope(selector) do
+ click_button(button)
+ end
+end
+
+When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector|
+ with_scope(selector) do
+ click_link(link)
+ end
+end
+
+When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field, value, selector|
+ with_scope(selector) do
+ fill_in(field, :with => value)
+ end
+end
+
+When /^(?:|I )fill in "([^"]*)" for "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector|
+ with_scope(selector) do
+ fill_in(field, :with => value)
+ end
+end
+
+# Use this to fill in an entire form with data from a table. Example:
+#
+# When I fill in the following:
+# | Account Number | 5002 |
+# | Expiry date | 2009-11-01 |
+# | Note | Nice guy |
+# | Wants Email? | |
+#
+# TODO: Add support for checkbox, select og option
+# based on naming conventions.
+#
+When /^(?:|I )fill in the following(?: within "([^"]*)")?:$/ do |selector, fields|
+ with_scope(selector) do
+ fields.rows_hash.each do |name, value|
+ When %{I fill in "#{name}" with "#{value}"}
+ end
+ end
+end
+
+When /^(?:|I )select "([^"]*)" from "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector|
+ with_scope(selector) do
+ select(value, :from => field)
+ end
+end
+
+When /^(?:|I )check "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector|
+ with_scope(selector) do
+ check(field)
+ end
+end
+
+When /^(?:|I )uncheck "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector|
+ with_scope(selector) do
+ uncheck(field)
+ end
+end
+
+When /^(?:|I )choose "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector|
+ with_scope(selector) do
+ choose(field)
+ end
+end
+
+When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"(?: within "([^"]*)")?$/ do |path, field, selector|
+ with_scope(selector) do
+ attach_file(field, path)
+ end
+end
+
+Then /^(?:|I )should see JSON:$/ do |expected_json|
+ require 'json'
+ expected = JSON.pretty_generate(JSON.parse(expected_json))
+ actual = JSON.pretty_generate(JSON.parse(response.body))
+ expected.should == actual
+end
+
+Then /^(?:|I )should see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector|
+ with_scope(selector) do
+ if page.respond_to? :should
+ page.should have_content(text)
+ else
+ assert page.has_content?(text)
+ end
+ end
+end
+
+Then /^(?:|I )should see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector|
+ regexp = Regexp.new(regexp)
+ with_scope(selector) do
+ if page.respond_to? :should
+ page.should have_xpath('//*', :text => regexp)
+ else
+ assert page.has_xpath?('//*', :text => regexp)
+ end
+ end
+end
+
+Then /^(?:|I )should not see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector|
+ with_scope(selector) do
+ if page.respond_to? :should
+ page.should have_no_content(text)
+ else
+ assert page.has_no_content?(text)
+ end
+ end
+end
+
+Then /^(?:|I )should not see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector|
+ regexp = Regexp.new(regexp)
+ with_scope(selector) do
+ if page.respond_to? :should
+ page.should have_no_xpath('//*', :text => regexp)
+ else
+ assert page.has_no_xpath?('//*', :text => regexp)
+ end
+ end
+end
+
+Then /^the "([^"]*)" field(?: within "([^"]*)")? should contain "([^"]*)"$/ do |field, selector, value|
+ with_scope(selector) do
+ field = find_field(field)
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
+ if field_value.respond_to? :should
+ field_value.should =~ /#{value}/
+ else
+ assert_match(/#{value}/, field_value)
+ end
+ end
+end
+
+Then /^the "([^"]*)" field(?: within "([^"]*)")? should not contain "([^"]*)"$/ do |field, selector, value|
+ with_scope(selector) do
+ field = find_field(field)
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
+ if field_value.respond_to? :should_not
+ field_value.should_not =~ /#{value}/
+ else
+ assert_no_match(/#{value}/, field_value)
+ end
+ end
+end
+
+Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should be checked$/ do |label, selector|
+ with_scope(selector) do
+ field_checked = find_field(label)['checked']
+ if field_checked.respond_to? :should
+ field_checked.should be_true
+ else
+ assert field_checked
+ end
+ end
+end
+
+Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should not be checked$/ do |label, selector|
+ with_scope(selector) do
+ field_checked = find_field(label)['checked']
+ if field_checked.respond_to? :should
+ field_checked.should be_false
+ else
+ assert !field_checked
+ end
+ end
+end
+
+Then /^(?:|I )should be on (.+)$/ do |page_name|
+ current_path = URI.parse(current_url).path
+ if current_path.respond_to? :should
+ current_path.should == path_to(page_name)
+ else
+ assert_equal path_to(page_name), current_path
+ end
+end
+
+Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
+ query = URI.parse(current_url).query
+ actual_params = query ? CGI.parse(query) : {}
+ expected_params = {}
+ expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
+
+ if actual_params.respond_to? :should
+ actual_params.should == expected_params
+ else
+ assert_equal expected_params, actual_params
+ end
+end
+
+Then /^I save and open the page$/ do
+ save_and_open_page
+end
+
+Then /^show me the page$/ do
+ save_and_open_page
+end
View
3 features/support/env.rb
@@ -0,0 +1,3 @@
+require 'capybara/cucumber'
+require 'test/unit/assertions'
+World(Test::Unit::Assertions)
View
35 features/support/paths.rb
@@ -0,0 +1,35 @@
+module NavigationHelpers
+ # Maps a name to a path. Used by the
+ #
+ # When /^I go to (.+)$/ do |page_name|
+ #
+ # step definition in web_steps.rb
+ #
+ def path_to(page_name)
+ case page_name
+
+ when /the new user page/
+ '/users/new'
+ when /the home\s?page/
+ '/'
+
+ # Add more mappings here.
+ # Here is an example that pulls values out of the Regexp:
+ #
+ # when /^(.*)'s profile page$/i
+ # user_profile_path(User.find_by_login($1))
+
+ else
+ begin
+ page_name =~ /the (.*) page/
+ path_components = $1.split(/\s+/)
+ self.send(path_components.push('path').join('_').to_sym)
+ rescue Object => e
+ raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
+ "Now, go and add a mapping in #{__FILE__}"
+ end
+ end
+ end
+end
+
+World(NavigationHelpers)
View
5 features/support/rails.rb
@@ -0,0 +1,5 @@
+PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
+TEMP_ROOT = File.join(PROJECT_ROOT, 'tmp').freeze
+APP_NAME = 'testapp'.freeze
+CUC_RAILS_ROOT = File.join(TEMP_ROOT, APP_NAME).freeze
+ENV['RAILS_ENV'] = 'test'
View
25 features/support/s3.rb
@@ -0,0 +1,25 @@
+module AWSS3Methods
+ def load_s3
+ begin
+ require 'aws/s3'
+ rescue LoadError => e
+ fail "You do not have aws-s3 installed."
+ end
+ end
+
+ def assert_credentials(key, secret)
+ load_s3
+ begin
+ AWS::S3::Base.establish_connection!(
+ :access_key_id => key,
+ :secret_access_key => secret
+ )
+ AWS::S3::Service.buckets
+ rescue AWS::S3::ResponseError => e
+ fail "Could not connect using AWS credentials in AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. " +
+ "Please make sure these are set in your environment."
+ end
+ end
+end
+
+World(AWSS3Methods)
View
20 gemfiles/rails2.gemfile
@@ -0,0 +1,20 @@
+# This file was generated by Appraisal
+
+source "http://rubygems.org"
+
+gem "activerecord", :require=>"active_record"
+gem "appraisal"
+gem "aws-s3", :require=>"aws/s3"
+gem "bundler"
+gem "cocaine", "~>0.2"
+gem "fog"
+gem "jruby-openssl", :platform=>:jruby
+gem "mime-types"
+gem "mocha"
+gem "rake"
+gem "rdoc", :require=>false
+gem "shoulda"
+gem "sqlite3", "~>1.3.4"
+gem "sprockets", "~> 2.0.0.beta.13", :require=>false
+gem "rails", "~> 2.3.12"
+
View
121 gemfiles/rails2.gemfile.lock
@@ -0,0 +1,121 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ actionmailer (2.3.12)
+ actionpack (= 2.3.12)
+ actionpack (2.3.12)
+ activesupport (= 2.3.12)
+ rack (~> 1.1.0)
+ activerecord (2.3.12)
+ activesupport (= 2.3.12)
+ activeresource (2.3.12)
+ activesupport (= 2.3.12)
+ activesupport (2.3.12)
+ appraisal (0.3.7)
+ aruba (~> 0.4.2)
+ bundler
+ rake
+ aruba (0.4.3)
+ bcat (>= 0.6.1)
+ childprocess (>= 0.1.9)
+ cucumber (>= 0.10.7)
+ rdiscount (>= 1.6.8)
+ rspec (>= 2.6.0)
+ aws-s3 (0.6.2)
+ builder
+ mime-types
+ xml-simple
+ bcat (0.6.1)
+ rack (~> 1.0)
+ bouncy-castle-java (1.5.0146.1)
+ builder (3.0.0)
+ childprocess (0.2.0)
+ ffi (~> 1.0.6)
+ cocaine (0.2.0)
+ cucumber (1.0.2)
+ builder (>= 2.1.2)
+ diff-lcs (>= 1.1.2)
+ gherkin (~> 2.4.5)
+ json (>= 1.4.6)
+ term-ansicolor (>= 1.0.5)
+ diff-lcs (1.1.2)
+ excon (0.6.5)
+ ffi (1.0.9)
+ ffi (1.0.9-java)
+ fog (0.9.0)
+ builder
+ excon (~> 0.6.1)
+ formatador (>= 0.1.3)
+ json
+ mime-types
+ net-scp (>= 1.0.4)
+ net-ssh (>= 2.1.4)
+ nokogiri (>= 1.4.4)
+ ruby-hmac
+ formatador (0.2.0)
+ gherkin (2.4.5)
+ json (>= 1.4.6)
+ gherkin (2.4.5-java)
+ json (>= 1.4.6)
+ hike (1.2.0)
+ jruby-openssl (0.7.4)
+ bouncy-castle-java
+ json (1.5.3)
+ json (1.5.3-java)
+ mime-types (1.16)
+ mocha (0.9.12)
+ net-scp (1.0.4)
+ net-ssh (>= 1.99.1)
+ net-ssh (2.1.4)
+ nokogiri (1.5.0)
+ nokogiri (1.5.0-java)
+ rack (1.1.2)
+ rails (2.3.12)
+ actionmailer (= 2.3.12)
+ actionpack (= 2.3.12)
+ activerecord (= 2.3.12)
+ activeresource (= 2.3.12)
+ activesupport (= 2.3.12)
+ rake (>= 0.8.3)
+ rake (0.9.2)
+ rdiscount (1.6.8)
+ rdoc (3.8)
+ rspec (2.6.0)
+ rspec-core (~> 2.6.0)
+ rspec-expectations (~> 2.6.0)
+ rspec-mocks (~> 2.6.0)
+ rspec-core (2.6.4)
+ rspec-expectations (2.6.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.6.0)
+ ruby-hmac (0.4.0)
+ shoulda (2.11.3)
+ sprockets (2.0.0.beta.13)
+ hike (~> 1.2)
+ rack (~> 1.0)
+ tilt (!= 1.3.0, ~> 1.1)
+ sqlite3 (1.3.4)
+ term-ansicolor (1.0.6)
+ tilt (1.3.2)
+ xml-simple (1.1.0)
+
+PLATFORMS
+ java
+ ruby
+
+DEPENDENCIES
+ activerecord
+ appraisal
+ aws-s3
+ bundler
+ cocaine (~> 0.2)
+ fog
+ jruby-openssl
+ mime-types
+ mocha
+ rails (~> 2.3.12)
+ rake
+ rdoc
+ shoulda
+ sprockets (~> 2.0.0.beta.13)
+ sqlite3 (~> 1.3.4)
View
20 gemfiles/rails3.gemfile
@@ -0,0 +1,20 @@
+# This file was generated by Appraisal
+
+source "http://rubygems.org"
+
+gem "activerecord", :require=>"active_record"
+gem "appraisal"
+gem "aws-s3", :require=>"aws/s3"
+gem "bundler"
+gem "cocaine", "~>0.2"
+gem "fog"
+gem "jruby-openssl", :platform=>:jruby
+gem "mime-types"
+gem "mocha"
+gem "rake"
+gem "rdoc", :require=>false
+gem "shoulda"
+gem "sqlite3", "~>1.3.4"
+gem "sprockets", "~> 2.0.0.beta.13", :require=>false
+gem "rails", "~> 3.0.9"
+
View
163 gemfiles/rails3.gemfile.lock
@@ -0,0 +1,163 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ abstract (1.0.0)
+ actionmailer (3.0.9)
+ actionpack (= 3.0.9)
+ mail (~> 2.2.19)
+ actionpack (3.0.9)
+ activemodel (= 3.0.9)
+ activesupport (= 3.0.9)
+ builder (~> 2.1.2)
+ erubis (~> 2.6.6)
+ i18n (~> 0.5.0)
+ rack (~> 1.2.1)
+ rack-mount (~> 0.6.14)
+ rack-test (~> 0.5.7)
+ tzinfo (~> 0.3.23)
+ activemodel (3.0.9)
+ activesupport (= 3.0.9)
+ builder (~> 2.1.2)
+ i18n (~> 0.5.0)
+ activerecord (3.0.9)
+ activemodel (= 3.0.9)
+ activesupport (= 3.0.9)
+ arel (~> 2.0.10)
+ tzinfo (~> 0.3.23)
+ activeresource (3.0.9)
+ activemodel (= 3.0.9)
+ activesupport (= 3.0.9)
+ activesupport (3.0.9)
+ appraisal (0.3.7)
+ aruba (~> 0.4.2)
+ bundler
+ rake
+ arel (2.0.10)
+ aruba (0.4.3)
+ bcat (>= 0.6.1)
+ childprocess (>= 0.1.9)
+ cucumber (>= 0.10.7)
+ rdiscount (>= 1.6.8)
+ rspec (>= 2.6.0)
+ aws-s3 (0.6.2)
+ builder
+ mime-types
+ xml-simple
+ bcat (0.6.1)
+ rack (~> 1.0)
+ bouncy-castle-java (1.5.0146.1)
+ builder (2.1.2)
+ childprocess (0.2.0)
+ ffi (~> 1.0.6)
+ cocaine (0.2.0)
+ cucumber (1.0.2)
+ builder (>= 2.1.2)
+ diff-lcs (>= 1.1.2)
+ gherkin (~> 2.4.5)
+ json (>= 1.4.6)
+ term-ansicolor (>= 1.0.5)
+ diff-lcs (1.1.2)
+ erubis (2.6.6)
+ abstract (>= 1.0.0)
+ excon (0.6.5)
+ ffi (1.0.9)
+ ffi (1.0.9-java)
+ fog (0.9.0)
+ builder
+ excon (~> 0.6.1)
+ formatador (>= 0.1.3)
+ json
+ mime-types
+ net-scp (>= 1.0.4)
+ net-ssh (>= 2.1.4)
+ nokogiri (>= 1.4.4)
+ ruby-hmac
+ formatador (0.2.0)
+ gherkin (2.4.5)
+ json (>= 1.4.6)
+ gherkin (2.4.5-java)
+ json (>= 1.4.6)
+ hike (1.2.0)
+ i18n (0.5.0)
+ jruby-openssl (0.7.4)
+ bouncy-castle-java
+ json (1.5.3)
+ json (1.5.3-java)
+ mail (2.2.19)
+ activesupport (>= 2.3.6)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.16)
+ mocha (0.9.12)
+ net-scp (1.0.4)
+ net-ssh (>= 1.99.1)
+ net-ssh (2.1.4)
+ nokogiri (1.5.0)
+ nokogiri (1.5.0-java)
+ polyglot (0.3.1)
+ rack (1.2.3)
+ rack-mount (0.6.14)
+ rack (>= 1.0.0)
+ rack-test (0.5.7)
+ rack (>= 1.0)
+ rails (3.0.9)
+ actionmailer (= 3.0.9)
+ actionpack (= 3.0.9)
+ activerecord (= 3.0.9)
+ activeresource (= 3.0.9)
+ activesupport (= 3.0.9)
+ bundler (~> 1.0)
+ railties (= 3.0.9)
+ railties (3.0.9)
+ actionpack (= 3.0.9)
+ activesupport (= 3.0.9)
+ rake (>= 0.8.7)
+ rdoc (~> 3.4)
+ thor (~> 0.14.4)
+ rake (0.9.2)
+ rdiscount (1.6.8)
+ rdoc (3.8)
+ rspec (2.6.0)
+ rspec-core (~> 2.6.0)
+ rspec-expectations (~> 2.6.0)
+ rspec-mocks (~> 2.6.0)
+ rspec-core (2.6.4)
+ rspec-expectations (2.6.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.6.0)
+ ruby-hmac (0.4.0)
+ shoulda (2.11.3)
+ sprockets (2.0.0.beta.13)
+ hike (~> 1.2)
+ rack (~> 1.0)
+ tilt (!= 1.3.0, ~> 1.1)
+ sqlite3 (1.3.4)
+ term-ansicolor (1.0.6)
+ thor (0.14.6)
+ tilt (1.3.2)
+ treetop (1.4.9)
+ polyglot (>= 0.3.1)
+ tzinfo (0.3.29)
+ xml-simple (1.1.0)
+
+PLATFORMS
+ java
+ ruby
+
+DEPENDENCIES
+ activerecord
+ appraisal
+ aws-s3
+ bundler
+ cocaine (~> 0.2)
+ fog
+ jruby-openssl
+ mime-types
+ mocha
+ rails (~> 3.0.9)
+ rake
+ rdoc
+ shoulda
+ sprockets (~> 2.0.0.beta.13)
+ sqlite3 (~> 1.3.4)
View
20 gemfiles/rails3_1.gemfile
@@ -0,0 +1,20 @@
+# This file was generated by Appraisal
+
+source "http://rubygems.org"
+
+gem "activerecord", :require=>"active_record"
+gem "appraisal"
+gem "aws-s3", :require=>"aws/s3"
+gem "bundler"
+gem "cocaine", "~>0.2"
+gem "fog"
+gem "jruby-openssl", :platform=>:jruby
+gem "mime-types"
+gem "mocha"
+gem "rake"
+gem "rdoc", :require=>false
+gem "shoulda"
+gem "sqlite3", "~>1.3.4"
+gem "sprockets", "~> 2.0.0.beta.13", :require=>false
+gem "rails", "~> 3.1.0.rc5"
+
View
172 gemfiles/rails3_1.gemfile.lock
@@ -0,0 +1,172 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ actionmailer (3.1.0.rc6)
+ actionpack (= 3.1.0.rc6)
+ mail (~> 2.3.0)
+ actionpack (3.1.0.rc6)
+ activemodel (= 3.1.0.rc6)
+ activesupport (= 3.1.0.rc6)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ i18n (~> 0.6)
+ rack (~> 1.3.2)
+ rack-cache (~> 1.0.2)
+ rack-mount (~> 0.8.1)
+ rack-test (~> 0.6.0)
+ sprockets (~> 2.0.0.beta.12)
+ activemodel (3.1.0.rc6)
+ activesupport (= 3.1.0.rc6)
+ bcrypt-ruby (~> 2.1.4)
+ builder (~> 3.0.0)
+ i18n (~> 0.6)
+ activerecord (3.1.0.rc6)
+ activemodel (= 3.1.0.rc6)
+ activesupport (= 3.1.0.rc6)
+ arel (~> 2.2.1)
+ tzinfo (~> 0.3.29)
+ activeresource (3.1.0.rc6)
+ activemodel (= 3.1.0.rc6)
+ activesupport (= 3.1.0.rc6)
+ activesupport (3.1.0.rc6)
+ multi_json (~> 1.0)
+ appraisal (0.3.7)
+ aruba (~> 0.4.2)
+ bundler
+ rake
+ arel (2.2.1)
+ aruba (0.4.5)
+ bcat (>= 0.6.1)
+ childprocess (>= 0.1.9)
+ cucumber (>= 0.10.7)
+ rdiscount (>= 1.6.8)
+ rspec (>= 2.6.0)
+ aws-s3 (0.6.2)
+ builder
+ mime-types
+ xml-simple
+ bcat (0.6.1)
+ rack (~> 1.0)
+ bcrypt-ruby (2.1.4)
+ bcrypt-ruby (2.1.4-java)
+ bouncy-castle-java (1.5.0146.1)
+ builder (3.0.0)
+ childprocess (0.2.0)
+ ffi (~> 1.0.6)
+ cocaine (0.2.0)
+ cucumber (1.0.2)
+ builder (>= 2.1.2)
+ diff-lcs (>= 1.1.2)
+ gherkin (~> 2.4.5)
+ json (>= 1.4.6)
+ term-ansicolor (>= 1.0.5)
+ diff-lcs (1.1.2)
+ erubis (2.7.0)
+ excon (0.6.5)
+ ffi (1.0.9)
+ ffi (1.0.9-java)
+ fog (0.10.0)
+ builder
+ excon (~> 0.6.5)
+ formatador (~> 0.2.0)
+ mime-types
+ multi_json (~> 1.0.3)
+ net-scp (~> 1.0.4)
+ net-ssh (~> 2.1.4)
+ nokogiri (~> 1.5.0)
+ ruby-hmac
+ formatador (0.2.0)
+ gherkin (2.4.5)
+ json (>= 1.4.6)
+ gherkin (2.4.5-java)
+ json (>= 1.4.6)
+ hike (1.2.1)
+ i18n (0.6.0)
+ jruby-openssl (0.7.4)
+ bouncy-castle-java
+ json (1.5.3)
+ json (1.5.3-java)
+ mail (2.3.0)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.16)
+ mocha (0.9.12)
+ multi_json (1.0.3)
+ net-scp (1.0.4)
+ net-ssh (>= 1.99.1)
+ net-ssh (2.1.4)
+ nokogiri (1.5.0)
+ nokogiri (1.5.0-java)
+ polyglot (0.3.2)
+ rack (1.3.2)
+ rack-cache (1.0.2)
+ rack (>= 0.4)
+ rack-mount (0.8.2)
+ rack (>= 1.0.0)
+ rack-ssl (1.3.2)
+ rack
+ rack-test (0.6.1)
+ rack (>= 1.0)
+ rails (3.1.0.rc6)
+ actionmailer (= 3.1.0.rc6)
+ actionpack (= 3.1.0.rc6)
+ activerecord (= 3.1.0.rc6)
+ activeresource (= 3.1.0.rc6)
+ activesupport (= 3.1.0.rc6)
+ bundler (~> 1.0)
+ railties (= 3.1.0.rc6)
+ railties (3.1.0.rc6)
+ actionpack (= 3.1.0.rc6)
+ activesupport (= 3.1.0.rc6)
+ rack-ssl (~> 1.3.2)
+ rake (>= 0.8.7)
+ rdoc (~> 3.4)
+ thor (~> 0.14.6)
+ rake (0.9.2)
+ rdiscount (1.6.8)
+ rdoc (3.9.2)
+ rspec (2.6.0)
+ rspec-core (~> 2.6.0)
+ rspec-expectations (~> 2.6.0)
+ rspec-mocks (~> 2.6.0)
+ rspec-core (2.6.4)
+ rspec-expectations (2.6.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.6.0)
+ ruby-hmac (0.4.0)
+ shoulda (2.11.3)
+ sprockets (2.0.0.beta.13)
+ hike (~> 1.2)
+ rack (~> 1.0)
+ tilt (~> 1.1, != 1.3.0)
+ sqlite3 (1.3.4)
+ term-ansicolor (1.0.6)
+ thor (0.14.6)
+ tilt (1.3.2)
+ treetop (1.4.10)
+ polyglot
+ polyglot (>= 0.3.1)
+ tzinfo (0.3.29)
+ xml-simple (1.1.0)
+
+PLATFORMS
+ java
+ ruby
+
+DEPENDENCIES
+ activerecord
+ appraisal
+ aws-s3
+ bundler
+ cocaine (~> 0.2)
+ fog
+ jruby-openssl
+ mime-types
+ mocha
+ rails (~> 3.1.0.rc5)
+ rake
+ rdoc
+ shoulda
+ sprockets (~> 2.0.0.beta.13)
+ sqlite3 (~> 1.3.4)
View
4 generators/paperclip/USAGE
@@ -1,5 +1,5 @@
Usage:
script/generate paperclip Class attachment1 (attachment2 ...)
-
-This will create a migration that will add the proper columns to your class's table.
+
+This will create a migration that will add the proper columns to your class's table.
View
16 generators/paperclip/paperclip_generator.rb
@@ -1,27 +1,27 @@
class PaperclipGenerator < Rails::Generator::NamedBase
attr_accessor :attachments, :migration_name
-
+
def initialize(args, options = {})
super
@class_name, @attachments = args[0], args[1..-1]
end
-
- def manifest
+
+ def manifest
file_name = generate_file_name
@migration_name = file_name.camelize
record do |m|
m.migration_template "paperclip_migration.rb.erb",
File.join('db', 'migrate'),
:migration_file_name => file_name
end
- end
-
- private
-
+ end
+
+ private
+
def generate_file_name
names = attachments.map{|a| a.underscore }
names = names[0..-2] + ["and", names[-1]] if names.length > 1
"add_attachments_#{names.join("_")}_to_#{@class_name.underscore}"
end
-
+
end
View
3 init.rb
@@ -1 +1,4 @@
require File.join(File.dirname(__FILE__), "lib", "paperclip")
+require 'paperclip/railtie'
+
+Paperclip::Railtie.insert
View
8 lib/generators/paperclip/USAGE
@@ -0,0 +1,8 @@
+Description:
+ Explain the generator
+
+Example:
+ rails generate paperclip Thing
+
+ This will create:
+ what/will/it/create
View
33 lib/generators/paperclip/paperclip_generator.rb
@@ -0,0 +1,33 @@
+require 'rails/generators/active_record'
+
+class PaperclipGenerator < ActiveRecord::Generators::Base
+ desc "Create a migration to add paperclip-specific fields to your model. " +
+ "The NAME argument is the name of your model, and the following " +
+ "arguments are the name of the attachments"
+
+ argument :attachment_names, :required => true, :type => :array, :desc => "The names of the attachment(s) to add.",
+ :banner => "attachment_one attachment_two attachment_three ..."
+
+ def self.source_root
+ @source_root ||= File.expand_path('../templates', __FILE__)
+ end
+
+ def generate_migration
+ migration_template "paperclip_migration.rb.erb", "db/migrate/#{migration_file_name}"
+ end
+
+ protected
+
+ def migration_name
+ "add_attachment_#{attachment_names.join("_")}_to_#{name.underscore}"
+ end
+
+ def migration_file_name
+ "#{migration_name}.rb"
+ end
+
+ def migration_class_name
+ migration_name.camelize
+ end
+
+end
View
19 lib/generators/paperclip/templates/paperclip_migration.rb.erb
@@ -0,0 +1,19 @@
+class <%= migration_class_name %> < ActiveRecord::Migration
+ def self.up
+<% attachment_names.each do |attachment| -%>
+ add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string
+ add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string
+ add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_size, :integer
+ add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at, :datetime
+<% end -%>
+ end
+
+ def self.down
+<% attachment_names.each do |attachment| -%>
+ remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_name
+ remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_content_type
+ remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_size
+ remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at
+<% end -%>
+ end
+end
View
253 lib/paperclip.rb
@@ -5,7 +5,7 @@
# columns to your table.
#
# Author:: Jon Yurek
-# Copyright:: Copyright (c) 2008-2009 thoughtbot, inc.
+# Copyright:: Copyright (c) 2008-2011 thoughtbot, inc.
# License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
#
# Paperclip defines an attachment as any file, though it makes special considerations
@@ -26,36 +26,35 @@
# See the +has_attached_file+ documentation for more details.
require 'erb'
+require 'digest'
require 'tempfile'
+require 'paperclip/version'
require 'paperclip/upfile'
require 'paperclip/iostream'
require 'paperclip/geometry'
require 'paperclip/processor'
require 'paperclip/thumbnail'
-require 'paperclip/storage'
require 'paperclip/interpolations'
require 'paperclip/style'
require 'paperclip/attachment'
-if defined? RAILS_ROOT
- Dir.glob(File.join(File.expand_path(RAILS_ROOT), "lib", "paperclip_processors", "*.rb")).each do |processor|
- require processor
- end
-end
+require 'paperclip/storage'
+require 'paperclip/callback_compatibility'
+require 'paperclip/railtie'
+require 'logger'
+require 'cocaine'
# The base module that gets included in ActiveRecord::Base. See the
# documentation for Paperclip::ClassMethods for more useful information.
module Paperclip
- VERSION = "2.3.1.1"
-
class << self
# Provides configurability to Paperclip. There are a number of options available, such as:
- # * whiny: Will raise an error if Paperclip cannot process thumbnails of
+ # * whiny: Will raise an error if Paperclip cannot process thumbnails of
# an uploaded image. Defaults to true.
# * log: Logs progress to the Rails log. Uses ActiveRecord's logger, so honors
# log levels, etc. Defaults to true.
# * command_path: Defines the path at which to find the command line
- # programs if they are not visible to Rails the system's search path. Defaults to
+ # programs if they are not visible to Rails the system's search path. Defaults to
# nil, which uses the first executable found in the user's search path.
# * image_magick_path: Deprecated alias of command_path.
def options
@@ -64,135 +63,177 @@ def options
:image_magick_path => nil,
:command_path => nil,
:log => true,
- :log_command => false,
+ :log_command => true,
:swallow_stderr => true
}
end
- def path_for_command command #:nodoc:
- if options[:image_magick_path]
- warn("[DEPRECATION] :image_magick_path is deprecated and will be removed. Use :command_path instead")
- end
- path = [options[:command_path] || options[:image_magick_path], command].compact
- File.join(*path)
+ def configure
+ yield(self) if block_given?
end
def interpolates key, &block
Paperclip::Interpolations[key] = block
end
- # The run method takes a command to execute and a string of parameters
+ # The run method takes a command to execute and an array of parameters
# that get passed to it. The command is prefixed with the :command_path
# option from Paperclip.options. If you have many commands to run and
# they are in different paths, the suggested course of action is to
# symlink them so they are all in the same directory.
#
# If the command returns with a result code that is not one of the
- # expected_outcodes, a PaperclipCommandLineError will be raised. Generally
+ # expected_outcodes, a Cocaine::CommandLineError will be raised. Generally
# a code of 0 is expected, but a list of codes may be passed if necessary.
+ # These codes should be passed as a hash as the last argument, like so:
+ #
+ # Paperclip.run("echo", "something", :expected_outcodes => [0,1,2,3])
#
- # This method can log the command being run when
+ # This method can log the command being run when
# Paperclip.options[:log_command] is set to true (defaults to false). This
# will only log if logging in general is set to true as well.
- def run cmd, params = "", expected_outcodes = 0
- command = %Q[#{path_for_command(cmd)} #{params}].gsub(/\s+/, " ")
- command = "#{command} 2>#{bit_bucket}" if Paperclip.options[:swallow_stderr]
- Paperclip.log(command) if Paperclip.options[:log_command]
- output = `#{command}`
- unless [expected_outcodes].flatten.include?($?.exitstatus)
- raise PaperclipCommandLineError, "Error while running #{cmd}"
- end
- output
- end
-
- def bit_bucket #:nodoc:
- File.exists?("/dev/null") ? "/dev/null" : "NUL"
- end
-
- def included base #:nodoc:
- base.extend ClassMethods
- unless base.respond_to?(:define_callbacks)
- base.send(:include, Paperclip::CallbackCompatability)
+ def run cmd, *params
+ if options[:image_magick_path]
+ Paperclip.log("[DEPRECATION] :image_magick_path is deprecated and will be removed. Use :command_path instead")
end
+ Cocaine::CommandLine.path = options[:command_path] || options[:image_magick_path]
+ Cocaine::CommandLine.new(cmd, *params).run
end
def processor name #:nodoc:
name = name.to_s.camelize
+ load_processor(name) unless Paperclip.const_defined?(name)
processor = Paperclip.const_get(name)
unless processor.ancestors.include?(Paperclip::Processor)
- raise PaperclipError.new("Processor #{name} was not found")
+ raise PaperclipError.new("Processor #{name} was not found")
end
processor
end
+ def load_processor(name)
+ if defined?(Rails.root) && Rails.root
+ require File.expand_path(Rails.root.join("lib", "paperclip_processors", "#{name.underscore}.rb"))
+ end
+ end
+
+ def each_instance_with_attachment(klass, name)
+ class_for(klass).all.each do |instance|
+ yield(instance) if instance.send(:"#{name}?")
+ end
+ end
+
# Log a paperclip-specific line. Uses ActiveRecord::Base.logger
# by default. Set Paperclip.options[:log] to false to turn off.
def log message
logger.info("[paperclip] #{message}") if logging?
end
def logger #:nodoc:
- ActiveRecord::Base.logger
+ @logger ||= options[:logger] || Logger.new(STDOUT)
+ end
+
+ def logger=(logger)
+ @logger = logger
end
def logging? #:nodoc:
options[:log]
end
+
+ def class_for(class_name)
+ # Ruby 1.9 introduces an inherit argument for Module#const_get and
+ # #const_defined? and changes their default behavior.
+ # https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L89
+ if Module.method(:const_get).arity == 1
+ class_name.split('::').inject(Object) do |klass, partial_class_name|
+ klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name) : klass.const_missing(partial_class_name)
+ end
+ else
+ class_name.split('::').inject(Object) do |klass, partial_class_name|
+ klass.const_defined?(partial_class_name) ? klass.const_get(partial_class_name, false) : klass.const_missing(partial_class_name)
+ end
+ end
+ rescue ArgumentError => e
+ # Sadly, we need to capture ArguementError here because Rails 2.3.x
+ # Active Support dependency's management will try to the constant inherited
+ # from Object, and fail misably with "Object is not missing constant X" error
+ # https://github.com/rails/rails/blob/v2.3.12/activesupport/lib/active_support/dependencies.rb#L124
+ if e.message =~ /is not missing constant/
+ raise NameError, "uninitialized constant #{class_name}"
+ else
+ raise e
+ end
+ end
end
class PaperclipError < StandardError #:nodoc:
end
- class PaperclipCommandLineError < StandardError #:nodoc:
+ class StorageMethodNotFound < PaperclipError
+ end
+
+ class CommandNotFoundError < PaperclipError
end
class NotIdentifiedByImageMagickError < PaperclipError #:nodoc:
end
-
+
class InfiniteInterpolationError < PaperclipError #:nodoc:
end
+ module Glue
+ def self.included base #:nodoc:
+ base.extend ClassMethods
+ base.class_attribute :attachment_definitions if base.respond_to?(:class_attribute)
+ if base.respond_to?(:set_callback)
+ base.send :include, Paperclip::CallbackCompatability::Rails3
+ else
+ base.send :include, Paperclip::CallbackCompatability::Rails21
+ end
+ end
+ end
+
module ClassMethods
# +has_attached_file+ gives the class it is called on an attribute that maps to a file. This
- # is typically a file stored somewhere on the filesystem and has been uploaded by a user.
+ # is typically a file stored somewhere on the filesystem and has been uploaded by a user.
# The attribute returns a Paperclip::Attachment object which handles the management of
- # that file. The intent is to make the attachment as much like a normal attribute. The
- # thumbnails will be created when the new file is assigned, but they will *not* be saved
- # until +save+ is called on the record. Likewise, if the attribute is set to +nil+ is
- # called on it, the attachment will *not* be deleted until +save+ is called. See the
- # Paperclip::Attachment documentation for more specifics. There are a number of options
+ # that file. The intent is to make the attachment as much like a normal attribute. The
+ # thumbnails will be created when the new file is assigned, but they will *not* be saved
+ # until +save+ is called on the record. Likewise, if the attribute is set to +nil+ is
+ # called on it, the attachment will *not* be deleted until +save+ is called. See the
+ # Paperclip::Attachment documentation for more specifics. There are a number of options
# you can set to change the behavior of a Paperclip attachment:
# * +url+: The full URL of where the attachment is publically accessible. This can just
# as easily point to a directory served directly through Apache as it can to an action
# that can control permissions. You can specify the full domain and path, but usually
- # just an absolute path is sufficient. The leading slash *must* be included manually for
- # absolute paths. The default value is
+ # just an absolute path is sufficient. The leading slash *must* be included manually for
+ # absolute paths. The default value is
# "/system/:attachment/:id/:style/:filename". See
# Paperclip::Attachment#interpolate for more information on variable interpolaton.
# :url => "/:class/:attachment/:id/:style_:filename"
# :url => "http://some.other.host/stuff/:class/:id_:extension"
- # * +default_url+: The URL that will be returned if there is no attachment assigned.
- # This field is interpolated just as the url is. The default value is
+ # * +default_url+: The URL that will be returned if there is no attachment assigned.
+ # This field is interpolated just as the url is. The default value is
# "/:attachment/:style/missing.png"
# has_attached_file :avatar, :default_url => "/images/default_:style_avatar.png"
# User.new.avatar_url(:small) # => "/images/default_small_avatar.png"
- # * +styles+: A hash of thumbnail styles and their geometries. You can find more about
- # geometry strings at the ImageMagick website
+ # * +styles+: A hash of thumbnail styles and their geometries. You can find more about
+ # geometry strings at the ImageMagick website
# (http://www.imagemagick.org/script/command-line-options.php#resize). Paperclip
- # also adds the "#" option (e.g. "50x50#"), which will resize the image to fit maximally
- # inside the dimensions and then crop the rest off (weighted at the center). The
+ # also adds the "#" option (e.g. "50x50#"), which will resize the image to fit maximally
+ # inside the dimensions and then crop the rest off (weighted at the center). The
# default value is to generate no thumbnails.
- # * +default_style+: The thumbnail style that will be used by default URLs.
+ # * +default_style+: The thumbnail style that will be used by default URLs.
# Defaults to +original+.
# has_attached_file :avatar, :styles => { :normal => "100x100#" },
# :default_style => :normal
# user.avatar.url # => "/avatars/23/normal_me.png"
# * +whiny+: Will raise an error if Paperclip cannot post_process an uploaded file due
- # to a command line error. This will override the global setting for this attachment.
+ # to a command line error. This will override the global setting for this attachment.
# Defaults to true. This option used to be called :whiny_thumbanils, but this is
# deprecated.
# * +convert_options+: When creating thumbnails, use this free-form options
- # field to pass in various convert command options. Typical options are "-strip" to
+ # array to pass in various convert command options. Typical options are "-strip" to
# remove all Exif data from the image (save space for thumbnails and avatars) or
# "-depth 8" to specify the bit depth of the resulting conversion. See ImageMagick
# convert documentation for more options: (http://www.imagemagick.org/script/convert.php)
@@ -208,23 +249,32 @@ module ClassMethods
# }
# NOTE: While not deprecated yet, it is not recommended to specify options this way.
# It is recommended that :convert_options option be included in the hash passed to each
- # :styles for compatability with future versions.
+ # :styles for compatibility with future versions.
+ # NOTE: Strings supplied to :convert_options are split on space in order to undergo
+ # shell quoting for safety. If your options require a space, please pre-split them
+ # and pass an array to :convert_options instead.
# * +storage+: Chooses the storage backend where the files will be stored. The current
# choices are :filesystem and :s3. The default is :filesystem. Make sure you read the
# documentation for Paperclip::Storage::Filesystem and Paperclip::Storage::S3
# for backend-specific options.
def has_attached_file name, options = {}
include InstanceMethods
- write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil?
+ if attachment_definitions.nil?
+ if respond_to?(:class_attribute)
+ self.attachment_definitions = {}
+ else
+ write_inheritable_attribute(:attachment_definitions, {})
+ end
+ end
+
attachment_definitions[name] = {:validations => []}.merge(options)
after_save :save_attached_files
before_destroy :destroy_attached_files
- define_callbacks :before_post_process, :after_post_process
- define_callbacks :"before_#{name}_post_process", :"after_#{name}_post_process"
-
+ define_paperclip_callbacks :post_process, :"#{name}_post_process"
+
define_method name do |*args|
a = attachment_for(name)
(args.length > 0) ? a.to_s(args.first) : a
@@ -257,14 +307,16 @@ def validates_attachment_size name, options = {}
min = options[:greater_than] || (options[:in] && options[:in].first) || 0
max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
range = (min..max)
- message = options[:message] || "file size must be between :min and :max bytes."
+ message = options[:message] || "file size must be between :min and :max bytes"
+ message = message.call if message.respond_to?(:call)
message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
validates_inclusion_of :"#{name}_file_size",
- :in => range,
- :message => message,
- :if => options[:if],
- :unless => options[:unless]
+ :in => range,
+ :message => message,
+ :if => options[:if],
+ :unless => options[:unless],
+ :allow_nil => true
end
# Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true.
@@ -281,20 +333,20 @@ def validates_attachment_thumbnails name, options = {}
# be run is this lambda or method returns true.
# * +unless+: Same as +if+ but validates if lambda or method returns false.
def validates_attachment_presence name, options = {}
- message = options[:message] || "must be set."
- validates_presence_of :"#{name}_file_name",
- :message => message,
- :if => options[:if],