dm-paperclip 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -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
+
@@ -0,0 +1,103 @@
1
+ h1. DataMapper Paperclip
2
+
3
+ DM-Paperclip is a port of Thoughtbot's Paperclip plugin to work with DataMapper 0.9. This plugin is fully compatible with
4
+ the original ActiveRecord-oriented Paperclip. You could take an existing ActiveRecord database and use it with DataMapper.
5
+ The module also includes updates validation handling and automatic including of the necessary 'property' fields into
6
+ your model.
7
+
8
+ To use it within your models, you need to ensure the three database fields are included. They are {name}_file_name,
9
+ {name}_content_type, and {name}_file_size. The first two are strings, the final _file_size column is an integer. So
10
+ if your user model has an avatar field, then you would add avatar_file_name, avatar_content_type, and avatar_file_size.
11
+
12
+ As with the original Paperclip plugin, it allows processing of thumbnails at the time the record is saved though ImageMagick.
13
+ It processes the thumbnails through the command-line applications instead of using RMagick.
14
+
15
+ See the documentation for the +has_attached_file+ method for options.
16
+
17
+ h2. Code
18
+
19
+ The code DM-Paperclip is available at Github:
20
+
21
+ git clone git://github.com/krobertson/paperclip.git
22
+
23
+ It is regularly updated to keep in sync with the latest from Thoughtbot.
24
+
25
+ Releases are tagged within the repository and versioned the same as the original model. You can also get the latest release
26
+ packaged as a gem through Rubyforge:
27
+
28
+ sudo gem install dm-paperclip
29
+
30
+ h2. Usage
31
+
32
+ In your model:
33
+
34
+ class User
35
+ include DataMapper::Resource
36
+ include Paperclip::Resource
37
+ property :id, Integer, :serial => true
38
+ property :username, String
39
+ has_attached_file :avatar,
40
+ :styles => { :medium => "300x300>",
41
+ :thumb => "100x100>" }
42
+ end
43
+
44
+ Your database will need to add three columns, +avatar_file_name+ (varchar), +avatar_content_type+ (varchar), and
45
+ +avatar_file_size+ (integer). You can either add these manually, auto-migrate, or use the following migration:
46
+
47
+ migration( 1, :add_user_paperclip_fields ) do
48
+ up do
49
+ modify_table :users do
50
+ add_column :avatar_file_name, "varchar(255)"
51
+ add_column :avatar_content_type, "varchar(255)"
52
+ add_column :avatar_file_size, "integer"
53
+ end
54
+ end
55
+ down do
56
+ modify_table :users do
57
+ drop_columns :avatar_file_name, :avatar_content_type, :avatar_file_size
58
+ end
59
+ end
60
+ end
61
+
62
+ In your edit and new views:
63
+
64
+ <% form_for @user, { :action => url(:user), :multipart => true } do %>
65
+ <%= file_field :name => 'avatar' %>
66
+ <% end %>
67
+
68
+ In your controller:
69
+
70
+ def create
71
+ ...
72
+ @user.avatar = params[:avatar]
73
+ end
74
+
75
+ In your show view:
76
+
77
+ <%= image_tag @user.avatar.url %>
78
+ <%= image_tag @user.avatar.url(:medium) %>
79
+ <%= image_tag @user.avatar.url(:thumb) %>
80
+
81
+ The following validations are available:
82
+
83
+ validates_attachment_presence :avatar
84
+ validates_attachment_content_type :avatar, :content_type => "image/png"
85
+ validates_attachment_size :avatar, :in => 1..10240
86
+ validates_attachment_thumbnails :avatar
87
+
88
+ In order to use validations, you must have loaded the 'dm-validations' gem into your app
89
+ (available as a part of dm-more). If the gem isn't loaded before DM-Paperclip is loaded,
90
+ the validation methods will be excluded. You will also need to include DataMapper::Validate
91
+ into your mode:
92
+
93
+ class User
94
+ include DataMapper::Resource
95
+ include DataMapper::Validate
96
+ include Paperclip::Resource
97
+ property :id, Integer, :serial => true
98
+ property :username, String
99
+ has_attached_file :avatar,
100
+ :styles => { :medium => "300x300>",
101
+ :thumb => "100x100>" }
102
+ validates_attachment_size :avatar, :in => 1..5120
103
+ end
@@ -0,0 +1,99 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
7
+ require 'dm-validations'
8
+ require 'dm-paperclip'
9
+
10
+ desc 'Default: run unit tests.'
11
+ task :default => [:clean, :test]
12
+
13
+ # Test tasks
14
+ desc 'Test the DM-Paperclip library.'
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.libs << 'dm-paperclip'
17
+ t.pattern = 'test/**/test_*.rb'
18
+ t.verbose = true
19
+ end
20
+
21
+ # Console
22
+ desc "Open an irb session preloaded with this library"
23
+ task :console do
24
+ sh "irb -rubygems -r dm-validations -r dm-migrations -r ./lib/dm-paperclip.rb"
25
+ end
26
+
27
+ # Rdoc
28
+ desc 'Generate documentation for the paperclip plugin.'
29
+ Rake::RDocTask.new(:doc) do |rdoc|
30
+ rdoc.rdoc_dir = 'doc'
31
+ rdoc.title = 'DM-Paperclip'
32
+ rdoc.options << '--line-numbers' << '--inline-source'
33
+ rdoc.rdoc_files.include('README.textile')
34
+ rdoc.rdoc_files.include('lib/**/*.rb')
35
+ end
36
+
37
+ # Code coverage
38
+ task :coverage do
39
+ system("rm -fr coverage")
40
+ system("rcov test/test_*.rb")
41
+ system("open coverage/index.html")
42
+ end
43
+
44
+ # Clean house
45
+ desc 'Clean up files.'
46
+ task :clean do |t|
47
+ FileUtils.rm_rf "doc"
48
+ FileUtils.rm_rf "coverage"
49
+ FileUtils.rm_rf "tmp"
50
+ FileUtils.rm_rf "pkg"
51
+ FileUtils.rm_rf "log"
52
+ end
53
+
54
+ spec = Gem::Specification.new do |s|
55
+ s.name = "dm-paperclip"
56
+ s.version = Paperclip::VERSION
57
+ s.author = "Ken Robertson"
58
+ s.email = "ken@invalidlogic.com jyurek@thoughtbot.com"
59
+ s.homepage = "http://invalidlogic.com/dm-paperclip/"
60
+ s.platform = Gem::Platform::RUBY
61
+ s.summary = "File attachments as attributes for DataMapper, based on the original Paperclip by Jon Yurek at Thoughtbot"
62
+ s.files = FileList["README.textile",
63
+ "LICENSE",
64
+ "Rakefile",
65
+ "init.rb",
66
+ "{lib,tasks,test}/**/*"].to_a
67
+ s.require_path = "lib"
68
+ s.test_files = FileList["test/**/test_*.rb"].to_a
69
+ s.rubyforge_project = "dm-paperclip"
70
+ s.has_rdoc = true
71
+ s.extra_rdoc_files = ["README.textile"]
72
+ s.rdoc_options << '--line-numbers' << '--inline-source'
73
+ s.requirements << "ImageMagick"
74
+ s.requirements << "data_mapper"
75
+ end
76
+
77
+ Rake::GemPackageTask.new(spec) do |pkg|
78
+ pkg.need_tar = true
79
+ end
80
+
81
+ WIN32 = (PLATFORM =~ /win32|cygwin/) rescue nil
82
+ SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
83
+
84
+ desc "Install #{spec.name} #{spec.version}"
85
+ task :install => [ :package ] do
86
+ sh "#{SUDO} gem install pkg/#{spec.name}-#{spec.version} --no-update-sources", :verbose => false
87
+ end
88
+
89
+ desc "Release new version"
90
+ task :release => [:test, :gem] do
91
+ require 'rubygems'
92
+ require 'rubyforge'
93
+ r = RubyForge.new
94
+ r.login
95
+ r.add_release spec.rubyforge_project,
96
+ spec.name,
97
+ spec.version,
98
+ File.join("pkg", "#{spec.name}-#{spec.version}.gem")
99
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'dm-paperclip')
2
+ File.send :include, Paperclip::Upfile
@@ -0,0 +1,229 @@
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 File.join(File.dirname(__FILE__), 'dm-paperclip', 'upfile')
30
+ require File.join(File.dirname(__FILE__), 'dm-paperclip', 'iostream')
31
+ require File.join(File.dirname(__FILE__), 'dm-paperclip', 'geometry')
32
+ require File.join(File.dirname(__FILE__), 'dm-paperclip', 'thumbnail')
33
+ require File.join(File.dirname(__FILE__), 'dm-paperclip', 'storage')
34
+ require File.join(File.dirname(__FILE__), 'dm-paperclip', 'attachment')
35
+
36
+ # Only include validations if dm-validations is loaded
37
+ require File.join(File.dirname(__FILE__), 'dm-paperclip', 'validations') unless defined?(DataMapper::Validate).nil?
38
+
39
+ module Paperclip
40
+ VERSION = "2.1.2"
41
+ class << self
42
+ # Provides configurability to Paperclip. There are a number of options available, such as:
43
+ # * whiny_thumbnails: Will raise an error if Paperclip cannot process thumbnails of
44
+ # an uploaded image. Defaults to true.
45
+ # * image_magick_path: Defines the path at which to find the +convert+ and +identify+
46
+ # programs if they are not visible to Rails the system's search path. Defaults to
47
+ # nil, which uses the first executable found in the search path.
48
+ def options
49
+ @options ||= {
50
+ :whiny_thumbnails => true,
51
+ :image_magick_path => nil
52
+ }
53
+ end
54
+
55
+ def path_for_command command #:nodoc:
56
+ path = [options[:image_magick_path], command].compact
57
+ File.join(*path)
58
+ end
59
+ end
60
+
61
+ class PaperclipError < StandardError #:nodoc:
62
+ end
63
+
64
+ class NotIdentifiedByImageMagickError < PaperclipError #:nodoc:
65
+ end
66
+
67
+ module Resource
68
+ def self.included(base)
69
+ base.extend Paperclip::ClassMethods
70
+ end
71
+ end
72
+
73
+ module ClassMethods
74
+ @@attachment_definitions = {}
75
+
76
+ # +has_attached_file+ gives the class it is called on an attribute that maps to a file. This
77
+ # is typically a file stored somewhere on the filesystem and has been uploaded by a user.
78
+ # The attribute returns a Paperclip::Attachment object which handles the management of
79
+ # that file. The intent is to make the attachment as much like a normal attribute. The
80
+ # thumbnails will be created when the new file is assigned, but they will *not* be saved
81
+ # until +save+ is called on the record. Likewise, if the attribute is set to +nil+ is
82
+ # called on it, the attachment will *not* be deleted until +save+ is called. See the
83
+ # Paperclip::Attachment documentation for more specifics. There are a number of options
84
+ # you can set to change the behavior of a Paperclip attachment:
85
+ # * +url+: The full URL of where the attachment is publically accessible. This can just
86
+ # as easily point to a directory served directly through Apache as it can to an action
87
+ # that can control permissions. You can specify the full domain and path, but usually
88
+ # just an absolute path is sufficient. The leading slash must be included manually for
89
+ # absolute paths. The default value is "/:class/:attachment/:id/:style_:filename". See
90
+ # Paperclip::Attachment#interpolate for more information on variable interpolaton.
91
+ # :url => "/:attachment/:id/:style_:basename:extension"
92
+ # :url => "http://some.other.host/stuff/:class/:id_:extension"
93
+ # * +default_url+: The URL that will be returned if there is no attachment assigned.
94
+ # This field is interpolated just as the url is. The default value is
95
+ # "/:class/:attachment/missing_:style.png"
96
+ # has_attached_file :avatar, :default_url => "/images/default_:style_avatar.png"
97
+ # User.new.avatar_url(:small) # => "/images/default_small_avatar.png"
98
+ # * +styles+: A hash of thumbnail styles and their geometries. You can find more about
99
+ # geometry strings at the ImageMagick website
100
+ # (http://www.imagemagick.org/script/command-line-options.php#resize). Paperclip
101
+ # also adds the "#" option (e.g. "50x50#"), which will resize the image to fit maximally
102
+ # inside the dimensions and then crop the rest off (weighted at the center). The
103
+ # default value is to generate no thumbnails.
104
+ # * +default_style+: The thumbnail style that will be used by default URLs.
105
+ # Defaults to +original+.
106
+ # has_attached_file :avatar, :styles => { :normal => "100x100#" },
107
+ # :default_style => :normal
108
+ # user.avatar.url # => "/avatars/23/normal_me.png"
109
+ # * +path+: The location of the repository of attachments on disk. This can be coordinated
110
+ # with the value of the +url+ option to allow files to be saved into a place where Apache
111
+ # can serve them without hitting your app. Defaults to
112
+ # ":merb_root/public/:class/:attachment/:id/:style_:filename".
113
+ # By default this places the files in the app's public directory which can be served
114
+ # directly. If you are using capistrano for deployment, a good idea would be to
115
+ # make a symlink to the capistrano-created system directory from inside your app's
116
+ # public directory.
117
+ # See Paperclip::Attachment#interpolate for more information on variable interpolaton.
118
+ # :path => "/var/app/attachments/:class/:id/:style/:filename"
119
+ # * +whiny_thumbnails+: Will raise an error if Paperclip cannot process thumbnails of an
120
+ # uploaded image. This will ovrride the global setting for this attachment.
121
+ # Defaults to true.
122
+ def has_attached_file name, options = {}
123
+ include InstanceMethods
124
+
125
+ @@attachment_definitions = {} if @@attachment_definitions.nil?
126
+ @@attachment_definitions[name] = {:validations => []}.merge(options)
127
+
128
+ property_options = options.delete_if { |k,v| ![ :public, :protected, :private, :accessor, :reader, :writer ].include?(key) }
129
+
130
+ property "#{name}_file_name".to_sym, String, property_options
131
+ property "#{name}_content_type".to_sym, String, property_options
132
+ property "#{name}_file_size".to_sym, Integer, property_options
133
+
134
+ after :save, :save_attached_files
135
+ before :destroy, :destroy_attached_files
136
+
137
+ define_method name do |*args|
138
+ a = attachment_for(name)
139
+ (args.length > 0) ? a.to_s(args.first) : a
140
+ end
141
+
142
+ define_method "#{name}=" do |file|
143
+ attachment_for(name).assign(file)
144
+ end
145
+
146
+ define_method "#{name}?" do
147
+ ! attachment_for(name).original_filename.blank?
148
+ end
149
+
150
+ unless defined?(DataMapper::Validate).nil?
151
+ add_validator_to_context(opts_from_validator_args([name]), [name], Paperclip::Validate::CopyAttachmentErrors)
152
+ end
153
+ end
154
+
155
+ unless defined?(DataMapper::Validate).nil?
156
+
157
+ # Places ActiveRecord-style validations on the size of the file assigned. The
158
+ # possible options are:
159
+ # * +in+: a Range of bytes (i.e. +1..1.megabyte+),
160
+ # * +less_than+: equivalent to :in => 0..options[:less_than]
161
+ # * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
162
+ # * +message+: error message to display, use :min and :max as replacements
163
+ def validates_attachment_size(*fields)
164
+ opts = opts_from_validator_args(fields)
165
+ add_validator_to_context(opts, fields, Paperclip::Validate::SizeValidator)
166
+ end
167
+
168
+ # Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true.
169
+ def validates_attachment_thumbnails name, options = {}
170
+ @@attachment_definitions[name][:whiny_thumbnails] = true
171
+ end
172
+
173
+ # Places ActiveRecord-style validations on the presence of a file.
174
+ def validates_attachment_presence(*fields)
175
+ opts = opts_from_validator_args(fields)
176
+ add_validator_to_context(opts, fields, Paperclip::Validate::RequiredFieldValidator)
177
+ end
178
+
179
+ # Places ActiveRecord-style validations on the content type of the file assigned. The
180
+ # possible options are:
181
+ # * +content_type+: Allowed content types. Can be a single content type or an array. Allows all by default.
182
+ # * +message+: The message to display when the uploaded file has an invalid content type.
183
+ def validates_attachment_content_type(*fields)
184
+ opts = opts_from_validator_args(fields)
185
+ add_validator_to_context(opts, fields, Paperclip::Validate::ContentTypeValidator)
186
+ end
187
+
188
+ end
189
+
190
+ # Returns the attachment definitions defined by each call to has_attached_file.
191
+ def attachment_definitions
192
+ @@attachment_definitions
193
+ end
194
+
195
+ end
196
+
197
+ module InstanceMethods #:nodoc:
198
+ def attachment_for name
199
+ @attachments ||= {}
200
+ @attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
201
+ end
202
+
203
+ def each_attachment
204
+ self.class.attachment_definitions.each do |name, definition|
205
+ yield(name, attachment_for(name))
206
+ end
207
+ end
208
+
209
+ def save_attached_files
210
+ each_attachment do |name, attachment|
211
+ attachment.send(:save)
212
+ end
213
+ end
214
+
215
+ def destroy_attached_files
216
+ each_attachment do |name, attachment|
217
+ attachment.queue_existing_for_delete
218
+ attachment.flush_deletes
219
+ end
220
+ end
221
+ end
222
+
223
+ end
224
+
225
+ # Set it all up.
226
+ if Object.const_defined?("ActiveRecord")
227
+ ActiveRecord::Base.send(:include, Paperclip)
228
+ File.send(:include, Paperclip::Upfile)
229
+ end