attachment-san 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +44 -0
- data/README +10 -0
- data/Rakefile +31 -0
- data/TODO +6 -0
- data/VERSION +1 -0
- data/attachment-san.gemspec +73 -0
- data/examples/photo.rb +41 -0
- data/examples/photos_controller.rb +22 -0
- data/examples/post.rb +3 -0
- data/lib/attachment_san/has.rb +49 -0
- data/lib/attachment_san/test/helper.rb +49 -0
- data/lib/attachment_san/test/rails.png +0 -0
- data/lib/attachment_san/variant.rb +153 -0
- data/lib/attachment_san.rb +58 -0
- data/napkin.rb +41 -0
- data/rails/init.rb +4 -0
- data/test/attachment_san_test.rb +148 -0
- data/test/fixtures/models/attachment.rb +20 -0
- data/test/fixtures/models/document.rb +39 -0
- data/test/fixtures/models/options_stub.rb +20 -0
- data/test/has_test.rb +116 -0
- data/test/lib/schema.rb +14 -0
- data/test/test_helper.rb +66 -0
- data/test/variant_test.rb +167 -0
- metadata +92 -0
data/LICENSE
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
Copyright (c) 2008 Manfred Stienstra, Fingertips <manfred@fngtps.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
19
|
+
|
20
|
+
--------------------------------------------------------------------------------
|
21
|
+
|
22
|
+
Some parts of attachment-san were originally included in ‘attachment_fu’ and
|
23
|
+
‘acts_as_attachment’ under the following license:
|
24
|
+
|
25
|
+
Copyright (c) 2005 Rick Olson
|
26
|
+
|
27
|
+
Unless noted specifically, all plugins in this repository are MIT licensed:
|
28
|
+
|
29
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
30
|
+
this software and associated documentation files (the "Software"), to deal in
|
31
|
+
the Software without restriction, including without limitation the rights to
|
32
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
33
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
34
|
+
subject to the following conditions:
|
35
|
+
|
36
|
+
The above copyright notice and this permission notice shall be included in all
|
37
|
+
copies or substantial portions of the Software.
|
38
|
+
|
39
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
40
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
41
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
42
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
43
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
44
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Attachment-San
|
2
|
+
|
3
|
+
Plugin for easy and rich attachment manipulation.
|
4
|
+
|
5
|
+
Although the ideas behind Attachment-San are different, it borrows heavily on both attachment_fu and acts_as_attachment by Rick Olson.
|
6
|
+
|
7
|
+
Dependencies
|
8
|
+
|
9
|
+
General: A recent Rails (at least 2.0, though it's most often tested on Edge)
|
10
|
+
Development: test/spec and SQLite3
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rake/rdoctask'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
desc "Run all tests by default"
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
Rake::TestTask.new do |t|
|
8
|
+
t.test_files = FileList['test/**/*_test.rb']
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'jeweler'
|
14
|
+
Jeweler::Tasks.new do |s|
|
15
|
+
s.name = "attachment-san"
|
16
|
+
s.homepage = "http://github.com/Fingertips/attachment-san"
|
17
|
+
s.email = ["eloy@fngtps.com", "manfred@fngtps.com"]
|
18
|
+
s.authors = ["Eloy Duran", "Manfred Stienstra"]
|
19
|
+
s.summary = s.description = "Rails plugin for easy and rich attachment manipulation."
|
20
|
+
s.files -= %w{ .gitignore .kick }
|
21
|
+
end
|
22
|
+
rescue LoadError
|
23
|
+
end
|
24
|
+
|
25
|
+
namespace :docs do
|
26
|
+
Rake::RDocTask.new(:generate) do |rd|
|
27
|
+
rd.main = "README"
|
28
|
+
rd.rdoc_files.include("README", "LICENSE", "lib/**/*.rb")
|
29
|
+
rd.options << "--all" << "--charset" << "utf-8"
|
30
|
+
end
|
31
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
* Make it actually work with more than one attachment class.
|
2
|
+
* has_attachment :watermark, :superclass => WatermarkSuperclass
|
3
|
+
* has_attachment :watermark, :class => WatermarkClass
|
4
|
+
* Nest created classes under the superclass.
|
5
|
+
* Do we need to sanitize the original filename, eg lowercase.
|
6
|
+
* Make :filename_scheme => :record_identifier also use nesting under parent resources. Eg /resources/1/images/2/filename
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{attachment-san}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Eloy Duran", "Manfred Stienstra"]
|
12
|
+
s.date = %q{2009-11-19}
|
13
|
+
s.description = %q{Rails plugin for easy and rich attachment manipulation.}
|
14
|
+
s.email = ["eloy@fngtps.com", "manfred@fngtps.com"]
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"LICENSE",
|
21
|
+
"README",
|
22
|
+
"Rakefile",
|
23
|
+
"TODO",
|
24
|
+
"VERSION",
|
25
|
+
"examples/photo.rb",
|
26
|
+
"examples/photos_controller.rb",
|
27
|
+
"examples/post.rb",
|
28
|
+
"lib/attachment_san.rb",
|
29
|
+
"lib/attachment_san/has.rb",
|
30
|
+
"lib/attachment_san/test/helper.rb",
|
31
|
+
"lib/attachment_san/test/rails.png",
|
32
|
+
"lib/attachment_san/variant.rb",
|
33
|
+
"napkin.rb",
|
34
|
+
"rails/init.rb",
|
35
|
+
"test/attachment_san_test.rb",
|
36
|
+
"test/fixtures/models/attachment.rb",
|
37
|
+
"test/fixtures/models/document.rb",
|
38
|
+
"test/fixtures/models/options_stub.rb",
|
39
|
+
"test/has_test.rb",
|
40
|
+
"test/lib/schema.rb",
|
41
|
+
"test/test_helper.rb",
|
42
|
+
"test/variant_test.rb"
|
43
|
+
]
|
44
|
+
s.homepage = %q{http://github.com/Fingertips/attachment-san}
|
45
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubygems_version = %q{1.3.5}
|
48
|
+
s.summary = %q{Rails plugin for easy and rich attachment manipulation.}
|
49
|
+
s.test_files = [
|
50
|
+
"test/attachment_san_test.rb",
|
51
|
+
"test/fixtures/models/attachment.rb",
|
52
|
+
"test/fixtures/models/document.rb",
|
53
|
+
"test/fixtures/models/options_stub.rb",
|
54
|
+
"test/has_test.rb",
|
55
|
+
"test/lib/schema.rb",
|
56
|
+
"test/test_helper.rb",
|
57
|
+
"test/variant_test.rb",
|
58
|
+
"examples/photo.rb",
|
59
|
+
"examples/photos_controller.rb",
|
60
|
+
"examples/post.rb"
|
61
|
+
]
|
62
|
+
|
63
|
+
if s.respond_to? :specification_version then
|
64
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
65
|
+
s.specification_version = 3
|
66
|
+
|
67
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
68
|
+
else
|
69
|
+
end
|
70
|
+
else
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
data/examples/photo.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
class Photo < ActiveRecord::Base
|
2
|
+
attachment_san
|
3
|
+
after_upload :make_black
|
4
|
+
|
5
|
+
has_one :thumbnail, :class => 'Photo'
|
6
|
+
belongs_to :large_photo, :class => 'Photo'
|
7
|
+
before_create :do_create_thumbnail
|
8
|
+
|
9
|
+
def do_create_thumbnail
|
10
|
+
unless large_photo
|
11
|
+
create_thumbnail :uploaded_data = uploaded_data, :size => "<300x400"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# ---
|
17
|
+
|
18
|
+
class Photo < ActiveRecord::Base
|
19
|
+
has_one :thumbnail
|
20
|
+
has_one :full_size
|
21
|
+
|
22
|
+
def uploaded_data=(data)
|
23
|
+
build_thumbnail :uploaded_data => data
|
24
|
+
build_full_size :uploaded_data => data
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Attachment < ActiveRecord::Base
|
29
|
+
end
|
30
|
+
|
31
|
+
class Thumbnail < Attachment
|
32
|
+
attachment_san
|
33
|
+
|
34
|
+
def resize(image)
|
35
|
+
attachment.resize(image)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class FullSize < Attachment
|
40
|
+
attachment_san :keep_filenames => true
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class PhotosController < ActionController::Base
|
2
|
+
before_filter :find_post
|
3
|
+
|
4
|
+
def new
|
5
|
+
end
|
6
|
+
|
7
|
+
def create
|
8
|
+
@photo = @post.photos.build params[:photo]
|
9
|
+
if @photo.save
|
10
|
+
flash[:hilight] = dom_id(@photo)
|
11
|
+
redirect_to @post
|
12
|
+
else
|
13
|
+
render :new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def find_post
|
20
|
+
@post = Post.find params[:post_id]
|
21
|
+
end
|
22
|
+
end
|
data/examples/post.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module AttachmentSan
|
2
|
+
module Has
|
3
|
+
VALID_OPTIONS = [:name, :process, :class, :variants, :filename_scheme]
|
4
|
+
|
5
|
+
def has_attachment(name, options = {})
|
6
|
+
define_attachment_association :has_one, name, options
|
7
|
+
end
|
8
|
+
|
9
|
+
def has_attachments(name, options = {})
|
10
|
+
define_attachment_association :has_many, name, options
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def define_attachment_association(macro, name, options)
|
16
|
+
define_variants(name, extract_variant_options!(options))
|
17
|
+
send(macro, name, options) unless reflect_on_association(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def extract_variant_options!(options)
|
21
|
+
options.symbolize_keys!
|
22
|
+
variant_options = options.slice(*VALID_OPTIONS)
|
23
|
+
options.except!(*VALID_OPTIONS)
|
24
|
+
variant_options
|
25
|
+
end
|
26
|
+
|
27
|
+
def define_variants(name, options)
|
28
|
+
model = create_model(name)
|
29
|
+
|
30
|
+
original = { :class => Variant::Original }
|
31
|
+
original.merge!(options) unless options.has_key?(:variants)
|
32
|
+
model.send(:define_variant, :original, original)
|
33
|
+
|
34
|
+
if variants = options[:variants]
|
35
|
+
variants.each do |name, options|
|
36
|
+
model.send(:define_variant, name, options)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# TODO: Currently creates these classes in the top level namespace
|
42
|
+
def create_model(name)
|
43
|
+
name = name.to_s.classify
|
44
|
+
::Object.const_get(name)
|
45
|
+
rescue NameError
|
46
|
+
::Object.const_set name, Class.new(AttachmentSan.attachment_class)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module AttachmentSan
|
6
|
+
module UploadHelpers
|
7
|
+
# Generates a Tempfile object similar to the object you'd get from the standard library CGI
|
8
|
+
# module in a multipart request.
|
9
|
+
#
|
10
|
+
# Borrowed from action_controller/test_process.
|
11
|
+
class FakeUploadFile
|
12
|
+
# The filename, *not* including the path, of the "uploaded" file
|
13
|
+
attr_reader :original_filename
|
14
|
+
|
15
|
+
# The content type of the "uploaded" file
|
16
|
+
attr_reader :content_type
|
17
|
+
|
18
|
+
def initialize(path, content_type = Mime::TEXT, binary = false)
|
19
|
+
path = path.to_s
|
20
|
+
raise "#{path} file does not exist" unless File.exist?(path)
|
21
|
+
@content_type = content_type
|
22
|
+
@original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 }
|
23
|
+
@tempfile = Tempfile.new(@original_filename)
|
24
|
+
@tempfile.binmode if binary
|
25
|
+
FileUtils.copy_file(path, @tempfile.path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def path #:nodoc:
|
29
|
+
@tempfile.path
|
30
|
+
end
|
31
|
+
|
32
|
+
alias local_path path
|
33
|
+
|
34
|
+
def method_missing(method_name, *args, &block) #:nodoc:
|
35
|
+
@tempfile.send(method_name, *args, &block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def uploaded_file(filename, content_type)
|
40
|
+
FakeUploadFile.new(filename, content_type)
|
41
|
+
end
|
42
|
+
|
43
|
+
def rails_icon
|
44
|
+
uploaded_file(File.expand_path('../rails.png', __FILE__), 'image/png')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Test::Unit::TestCase.send(:include, AttachmentSan::UploadHelpers)
|
Binary file
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require "digest"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module AttachmentSan
|
5
|
+
class Variant
|
6
|
+
module ClassMethods
|
7
|
+
def self.extended(model)
|
8
|
+
model.class_inheritable_accessor :variant_reflections
|
9
|
+
model.variant_reflections = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def reflect_on_variant(name)
|
13
|
+
variant_reflections.find { |r| r[:name] == name.to_sym }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def define_variant(name, options_or_class_or_proc)
|
19
|
+
return if reflect_on_variant(name)
|
20
|
+
|
21
|
+
reflection =
|
22
|
+
case x = options_or_class_or_proc
|
23
|
+
when Hash
|
24
|
+
{ :class => Variant }.merge(x.symbolize_keys)
|
25
|
+
when Class
|
26
|
+
{ :class => x }
|
27
|
+
when Proc
|
28
|
+
{ :class => Variant, :process => x }
|
29
|
+
when nil
|
30
|
+
{ :class => const_get(name.to_s.camelize) }
|
31
|
+
else
|
32
|
+
raise TypeError, "Please specify a options hash, variant class, or process proc. Can't use `#{x.inspect}'."
|
33
|
+
end
|
34
|
+
|
35
|
+
reflection[:name] = name = name.to_sym
|
36
|
+
variant_reflections << reflection
|
37
|
+
|
38
|
+
# def original
|
39
|
+
# @original ||= begin
|
40
|
+
# reflection = self.class.reflect_on_variant(:original)
|
41
|
+
# reflection.klass.new(self, reflection)
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
class_eval <<-DEF, __FILE__, __LINE__ + 1
|
45
|
+
def #{name}
|
46
|
+
@#{name} ||= begin
|
47
|
+
reflection = self.class.reflect_on_variant(:#{name})
|
48
|
+
reflection[:class].new(self, reflection)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
DEF
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Variant
|
57
|
+
attr_reader :record, :reflection
|
58
|
+
|
59
|
+
def initialize(record, reflection)
|
60
|
+
@record, @reflection = record, reflection
|
61
|
+
end
|
62
|
+
|
63
|
+
def base_options
|
64
|
+
@record.class.attachment_san_options
|
65
|
+
end
|
66
|
+
|
67
|
+
def base_path
|
68
|
+
base_options[:base_path]
|
69
|
+
end
|
70
|
+
|
71
|
+
def public_base_path
|
72
|
+
base_options[:public_base_path]
|
73
|
+
end
|
74
|
+
|
75
|
+
def filename_scheme
|
76
|
+
@reflection[:filename_scheme] || base_options[:filename_scheme]
|
77
|
+
end
|
78
|
+
|
79
|
+
def original
|
80
|
+
@record.original
|
81
|
+
end
|
82
|
+
|
83
|
+
def name
|
84
|
+
@reflection[:name]
|
85
|
+
end
|
86
|
+
|
87
|
+
def extension
|
88
|
+
(ext = base_options[:extension]) == :keep_original ? @record.extension : ext
|
89
|
+
end
|
90
|
+
|
91
|
+
def token
|
92
|
+
@record.token.scan(/.{2}/)
|
93
|
+
end
|
94
|
+
|
95
|
+
def filename
|
96
|
+
unless @filename
|
97
|
+
@filename =
|
98
|
+
case filename_scheme
|
99
|
+
when :variant_name
|
100
|
+
name.to_s
|
101
|
+
when :keep_original
|
102
|
+
@record.filename
|
103
|
+
when :record_identifier
|
104
|
+
@record_class_name ||= @record.class.name.underscore.pluralize
|
105
|
+
"/#{@record_class_name}/#{@record.to_param}/#{name}"
|
106
|
+
when :token
|
107
|
+
File.join(token, "#{@record.filename_without_extension}.#{name}")
|
108
|
+
else
|
109
|
+
raise ArgumentError, "The :filename_scheme option should be one of `:token', `:filename_scheme', `:record_identifier', or `:variant_name', it currently is `#{filename_scheme.inspect}'."
|
110
|
+
end
|
111
|
+
@filename << ".#{extension}" unless extension.blank?
|
112
|
+
end
|
113
|
+
@filename
|
114
|
+
end
|
115
|
+
|
116
|
+
def file_path
|
117
|
+
File.join(base_path, filename)
|
118
|
+
end
|
119
|
+
|
120
|
+
def public_path
|
121
|
+
File.join(public_base_path, filename)
|
122
|
+
end
|
123
|
+
|
124
|
+
def dir_path
|
125
|
+
File.dirname(file_path)
|
126
|
+
end
|
127
|
+
|
128
|
+
def mkdir!
|
129
|
+
FileUtils.mkdir_p(dir_path)
|
130
|
+
end
|
131
|
+
|
132
|
+
def process!
|
133
|
+
mkdir!
|
134
|
+
@reflection[:process].call(self)
|
135
|
+
end
|
136
|
+
|
137
|
+
class Original < Variant
|
138
|
+
def original
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
def filename
|
143
|
+
@filename ||= (filename_scheme == :token ? File.join(token, @record.filename) : super)
|
144
|
+
end
|
145
|
+
|
146
|
+
def process!
|
147
|
+
return super if @reflection[:process]
|
148
|
+
mkdir!
|
149
|
+
File.open(file_path, 'w') { |f| f.write @record.uploaded_file.read }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "attachment_san/has"
|
2
|
+
require "attachment_san/variant"
|
3
|
+
|
4
|
+
module AttachmentSan
|
5
|
+
module Initializer
|
6
|
+
def attachment_san(options = {})
|
7
|
+
include AttachmentSan
|
8
|
+
|
9
|
+
opt = self.attachment_san_options = {
|
10
|
+
:public_base_path => '',
|
11
|
+
:extension => :keep_original,
|
12
|
+
:filename_scheme => :variant_name
|
13
|
+
}.merge(options)
|
14
|
+
# Defaults :base_path to expanded :public_base_path.
|
15
|
+
opt[:base_path] ||= Rails.root + File.join('public', opt[:public_base_path])
|
16
|
+
opt
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
mattr_accessor :attachment_class
|
21
|
+
|
22
|
+
def self.included(model)
|
23
|
+
self.attachment_class = model
|
24
|
+
model.extend Variant::ClassMethods
|
25
|
+
|
26
|
+
model.class_inheritable_accessor :attachment_san_options
|
27
|
+
model.define_callbacks :before_upload, :after_upload
|
28
|
+
model.after_create :process_variants!
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :uploaded_file
|
32
|
+
|
33
|
+
def uploaded_file=(uploaded_file)
|
34
|
+
callback :before_upload
|
35
|
+
|
36
|
+
@uploaded_file = uploaded_file
|
37
|
+
self.filename = uploaded_file.original_filename
|
38
|
+
self.content_type = uploaded_file.content_type
|
39
|
+
|
40
|
+
callback :after_upload
|
41
|
+
end
|
42
|
+
|
43
|
+
def extension
|
44
|
+
filename.split('.').last if filename.include?('.')
|
45
|
+
end
|
46
|
+
|
47
|
+
def filename_without_extension
|
48
|
+
filename.include?('.') ? filename.split('.')[0..-2].join('.') : filename
|
49
|
+
end
|
50
|
+
|
51
|
+
def variants
|
52
|
+
self.class.variant_reflections.map { |reflection| send(reflection[:name]) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def process_variants!
|
56
|
+
variants.each(&:process!)
|
57
|
+
end
|
58
|
+
end
|
data/napkin.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Variant:
|
2
|
+
# * Returns path information
|
3
|
+
# * /a2/e0/dd/fits_within_200x200.jpg - "many many files"
|
4
|
+
# * /organizations/12/logos/main.jpg - "one or two files"
|
5
|
+
# * /cant/guess/me/82734mhsdf923184jnshdf91238ukjhsdf.jpg - "secret location"
|
6
|
+
|
7
|
+
|
8
|
+
member.avatar.medium # => member.avatar.image('fits_within_8x8')
|
9
|
+
|
10
|
+
company.logo.original # =>
|
11
|
+
|
12
|
+
{ :company => { :logo => { :data => IOWEETIKVEEL } } }
|
13
|
+
|
14
|
+
# abstract model for data about a file, and has variants, defined by the model that has attachments
|
15
|
+
class Attachment
|
16
|
+
belongs_to :attachable
|
17
|
+
|
18
|
+
include Att # makes this an attachment model
|
19
|
+
end
|
20
|
+
|
21
|
+
class Member
|
22
|
+
extend Att::Has # adds declerative class methods
|
23
|
+
include Att::Variant # defines filters to handle variants
|
24
|
+
|
25
|
+
attaches_one :avatar
|
26
|
+
attaches_many
|
27
|
+
|
28
|
+
has_attachment :avatar, :variants => {
|
29
|
+
'medium' => { ... }, # => Class.new(Att:Variant)
|
30
|
+
'fits_within_80x80' => { :fits_within => [80, 80] }
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO
|
35
|
+
# * filters on a variant basis?
|
36
|
+
# * actual sizes
|
37
|
+
# * module Variants
|
38
|
+
# class Medium
|
39
|
+
# undef :foo
|
40
|
+
# end
|
41
|
+
# end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "AttachmentSan, concerning base options" do
|
4
|
+
default = OptionsStub.new_subclass do
|
5
|
+
attachment_san
|
6
|
+
end
|
7
|
+
|
8
|
+
default_with_public_path = OptionsStub.new_subclass do
|
9
|
+
attachment_san :public_base_path => '/files/assets'
|
10
|
+
end
|
11
|
+
|
12
|
+
specified = OptionsStub.new_subclass do
|
13
|
+
attachment_san :base_path => '/some/base/path', :public_base_path => '/files/assets', :extension => :png, :filename_scheme => :token
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should default the base_path to `public'" do
|
17
|
+
default.attachment_san_options[:base_path].should == Rails.root + 'public/'
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should use the specified base_path" do
|
21
|
+
specified.attachment_san_options[:base_path].should == '/some/base/path'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should default the public_base_path to an empty string" do
|
25
|
+
default.attachment_san_options[:public_base_path].should == ''
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should use the specified public_base_path" do
|
29
|
+
specified.attachment_san_options[:public_base_path].should == '/files/assets'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should default the base_path to `public' + public_base_path" do
|
33
|
+
default_with_public_path.attachment_san_options[:base_path].should == Rails.root + 'public/files/assets'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should default to use the variant's name as the filename for variants" do
|
37
|
+
default.attachment_san_options[:filename_scheme].should == :variant_name
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should use a token version for variant filenames" do
|
41
|
+
specified.attachment_san_options[:filename_scheme].should == :token
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should use the unique records identifier for variant filenames" do
|
45
|
+
OptionsStub.new_subclass do
|
46
|
+
attachment_san :filename_scheme => :record_identifier
|
47
|
+
end.attachment_san_options[:filename_scheme].should == :record_identifier
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should default to use the original file's extension for variants" do
|
51
|
+
default.attachment_san_options[:extension].should == :keep_original end
|
52
|
+
|
53
|
+
it "should use the specified file extension for variants" do
|
54
|
+
specified.attachment_san_options[:extension].should == :png
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "AttachmentSan, class methods" do
|
59
|
+
before do
|
60
|
+
@upload = rails_icon
|
61
|
+
@attachment = Attachment.new(:uploaded_file => @upload)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should assign the base attachment class when attachment_san is called" do
|
65
|
+
AttachmentSan.attachment_class.should.be Attachment
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should call a before_upload filter chain before actually assigning the new uploaded file" do
|
69
|
+
new_upload = rails_icon
|
70
|
+
@attachment.uploaded_file = new_upload
|
71
|
+
@attachment.file_before_upload.should.be @upload
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should call an after_upload filter chain after assigning the new uploaded file" do
|
75
|
+
@attachment.file_after_upload.should.be @upload
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should define a variant with options" do
|
79
|
+
Logo.variant_reflections.map { |r| r[:name] }.should == [:original, :header]
|
80
|
+
Logo.new.header.should.be.instance_of MyVariant
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "AttachmentSan, instance methods" do
|
85
|
+
before do
|
86
|
+
@upload = rails_icon
|
87
|
+
@attachment = Attachment.new(:uploaded_file => @upload)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should make a model accept a file upload" do
|
91
|
+
@attachment.uploaded_file.should.be @upload
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should assign the original filename to the model" do
|
95
|
+
@attachment.filename.should == @upload.original_filename
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should assign the content type to the model" do
|
99
|
+
@attachment.content_type.should == @upload.content_type
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should return the original file's extension" do
|
103
|
+
@attachment.extension.should == 'png'
|
104
|
+
|
105
|
+
@attachment.stubs(:filename).returns('Rakefile')
|
106
|
+
@attachment.extension.should.be nil
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should return the original filename without extension" do
|
110
|
+
@attachment.filename_without_extension.should == 'rails'
|
111
|
+
|
112
|
+
@attachment.stubs(:filename).returns('Rakefile')
|
113
|
+
@attachment.filename_without_extension.should == 'Rakefile'
|
114
|
+
|
115
|
+
@attachment.stubs(:filename).returns('some.dotted.spaced.file.txt')
|
116
|
+
@attachment.filename_without_extension.should == 'some.dotted.spaced.file'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "An AttachmentSan instance, concerning variants" do
|
121
|
+
before do
|
122
|
+
@upload = rails_icon
|
123
|
+
@document = Document.new
|
124
|
+
@document.build_logo :uploaded_file => @upload
|
125
|
+
@document.build_watermark :uploaded_file => @upload
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should call process on each of it's variants after create" do
|
129
|
+
@document.watermark.original.expects(:process!)
|
130
|
+
@document.logo.original.expects(:process!)
|
131
|
+
@document.logo.header.expects(:process!)
|
132
|
+
@document.save!
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should not call process when the model already exists" do
|
136
|
+
@document.save!
|
137
|
+
|
138
|
+
@document.watermark.original.expects(:process!).never
|
139
|
+
@document.logo.original.expects(:process!).never
|
140
|
+
@document.logo.header.expects(:process!).never
|
141
|
+
@document.save!
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should return it's variants" do
|
145
|
+
@document.watermark.variants.map(&:name).should == [:original]
|
146
|
+
@document.logo.variants.map(&:name).should == [:original, :header]
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Attachment < ActiveRecord::Base
|
2
|
+
attachment_san :base_path => TMP_DIR, :public_base_path => '/files/assets'
|
3
|
+
|
4
|
+
# Test code
|
5
|
+
|
6
|
+
def self.reset!
|
7
|
+
FileUtils.rm_rf TMP_DIR
|
8
|
+
FileUtils.mkdir_p TMP_DIR
|
9
|
+
end
|
10
|
+
|
11
|
+
# User code
|
12
|
+
|
13
|
+
belongs_to :attachable, :polymorphic => true
|
14
|
+
|
15
|
+
attr_accessor :file_before_upload
|
16
|
+
before_upload { |record| record.file_before_upload = record.uploaded_file }
|
17
|
+
|
18
|
+
attr_accessor :file_after_upload
|
19
|
+
after_upload { |record| record.file_after_upload = record.uploaded_file }
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class MyVariant < AttachmentSan::Variant
|
2
|
+
def process!
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
class MyProcessor
|
7
|
+
def initialize(variant)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class AddressCard < Attachment
|
12
|
+
class SmallCard < AttachmentSan::Variant
|
13
|
+
def process!
|
14
|
+
puts 'SMALL!'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class BigCard < AttachmentSan::Variant
|
19
|
+
def process!
|
20
|
+
puts 'BIG!'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Document < ActiveRecord::Base
|
26
|
+
extend AttachmentSan::Has
|
27
|
+
|
28
|
+
has_attachment :watermark
|
29
|
+
has_attachment :logo, :variants => { :header => MyVariant }
|
30
|
+
|
31
|
+
has_attachments :misc_files, :filename_scheme => :keep_original, :process => proc { :from_process_proc }
|
32
|
+
has_attachments :images, :variants => {
|
33
|
+
:thumbnail => proc { |v| MyProcessor.new(v) },
|
34
|
+
:medium_sized => proc {},
|
35
|
+
:download => proc {}
|
36
|
+
}
|
37
|
+
|
38
|
+
has_attachments :address_cards, :variants => [:small_card, :big_card]
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class OptionsStub
|
2
|
+
def self.new_subclass(&block)
|
3
|
+
before = AttachmentSan.attachment_class
|
4
|
+
model = Class.new(self)
|
5
|
+
model.instance_eval(&block)
|
6
|
+
AttachmentSan.attachment_class = before
|
7
|
+
model
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.define_callbacks(*args)
|
11
|
+
# no we don't!
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.after_create(m)
|
15
|
+
# no we don't!
|
16
|
+
end
|
17
|
+
|
18
|
+
class_inheritable_accessor :attachment_san_options
|
19
|
+
extend AttachmentSan::Initializer
|
20
|
+
end
|
data/test/has_test.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "AttachmentSan::Has" do
|
4
|
+
it "should pass on any unknown options to the has_one macro" do
|
5
|
+
Document.expects(:has_one).with(:other_file, :as => :attachable, :order => :updated_at)
|
6
|
+
Document.expects(:define_variants).with(:other_file, :process => proc {}, :class => MyVariant, :filename_scheme => :token, :variants => [:hoge, :fuga])
|
7
|
+
|
8
|
+
Document.has_attachment :other_file, :as => :attachable, :order => :updated_at,
|
9
|
+
:process => proc {}, :class => MyVariant, :filename_scheme => :token, :variants => [:hoge, :fuga]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should pass on any unknown options to the has_many macro" do
|
13
|
+
Document.expects(:has_many).with(:other_files, :as => :attachable, :order => :updated_at)
|
14
|
+
Document.expects(:define_variants).with(:other_files, :process => proc {}, :class => MyVariant, :filename_scheme => :token, :variants => [:hoge, :fuga])
|
15
|
+
|
16
|
+
Document.has_attachments :other_files, :as => :attachable, :order => :updated_at,
|
17
|
+
:process => proc {}, :class => MyVariant, :filename_scheme => :token, :variants => [:hoge, :fuga]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should not define an association if an association for the given name exists" do
|
21
|
+
Document.expects(:has_one).never
|
22
|
+
Document.expects(:define_variants).with(:watermark, {})
|
23
|
+
|
24
|
+
Document.expects(:has_many).never
|
25
|
+
Document.expects(:define_variants).with(:images, {})
|
26
|
+
|
27
|
+
Document.has_attachment :watermark, :as => :attachable
|
28
|
+
Document.has_attachments :images, :as => :attachable
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "AttachmentSan::Has, concerning a single associated attachment" do
|
33
|
+
before do
|
34
|
+
@document = Document.new
|
35
|
+
@document.build_logo :uploaded_file => rails_icon
|
36
|
+
@document.build_watermark :uploaded_file => rails_icon
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should create a model class and a has_one association" do
|
40
|
+
reflection = Document.reflect_on_association(:logo)
|
41
|
+
reflection.macro.should == :has_one
|
42
|
+
reflection.klass.should == Logo
|
43
|
+
Logo.superclass.should.be AttachmentSan.attachment_class
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should store the variant options on the new model class" do
|
47
|
+
Watermark.variant_reflections.length.should == 1
|
48
|
+
Watermark.variant_reflections.first[:name].should == :original
|
49
|
+
Watermark.variant_reflections.first[:class].should == AttachmentSan::Variant::Original
|
50
|
+
|
51
|
+
Logo.variant_reflections.length.should == 2
|
52
|
+
Logo.variant_reflections.last[:name].should == :header
|
53
|
+
Logo.variant_reflections.last[:class].should == MyVariant
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should define only a default original variant if no other variants are given" do
|
57
|
+
variant = @document.watermark.original
|
58
|
+
variant.name.should == :original
|
59
|
+
variant.should.be.instance_of AttachmentSan::Variant::Original
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should define a default original variant and the ones specified" do
|
63
|
+
%w{ original header }.each do |name|
|
64
|
+
variant = @document.logo.send(name)
|
65
|
+
variant.name.to_s.should == name
|
66
|
+
variant.should.be.instance_of Logo.reflect_on_variant(name)[:class]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "AttachmentSan::Has, concerning a collection of associated attachments" do
|
72
|
+
before do
|
73
|
+
@document = Document.new
|
74
|
+
2.times { @document.images.build :uploaded_file => rails_icon }
|
75
|
+
2.times { @document.misc_files.build :uploaded_file => rails_icon }
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should create a model class and a has_many association" do
|
79
|
+
reflection = Document.reflect_on_association(:images)
|
80
|
+
reflection.macro.should == :has_many
|
81
|
+
reflection.klass.should == Image
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should define only a default original variant if no others are given" do
|
85
|
+
variants = @document.misc_files.map(&:original)
|
86
|
+
variants.should.all { |v| v.name == :original }
|
87
|
+
variants.should.all { |v| v.instance_of? AttachmentSan::Variant }
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should define a default original variant and the ones specified" do
|
91
|
+
%w{ original thumbnail medium_sized download }.each do |name|
|
92
|
+
variants = @document.images.map(&name.to_sym)
|
93
|
+
variants.should.all { |v| v.name == name }
|
94
|
+
variants.should.all { |v| v.instance_of? AttachmentSan::Variant }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "AttachmentSan::Has, concerning attachment definitions with only a default original variant" do
|
100
|
+
it "should pass the options hash on to the variant" do
|
101
|
+
MiscFile.variant_reflections.length.should == 1
|
102
|
+
MiscFile.variant_reflections.first[:name].should == :original
|
103
|
+
MiscFile.variant_reflections.first[:class].should == AttachmentSan::Variant::Original
|
104
|
+
MiscFile.variant_reflections.first[:filename_scheme].should == :keep_original
|
105
|
+
MiscFile.variant_reflections.first[:process].call.should == :from_process_proc
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "AttachmentSan::Has, concerning attachment definitions with an array of variants" do
|
110
|
+
it "should simply assume the variant class exists" do
|
111
|
+
@document = Document.new
|
112
|
+
card = @document.address_cards.build(:uploaded_file => rails_icon)
|
113
|
+
card.small_card.should.be.instance_of AddressCard::SmallCard
|
114
|
+
card.big_card.should.be.instance_of AddressCard::BigCard
|
115
|
+
end
|
116
|
+
end
|
data/test/lib/schema.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
ActiveRecord::Migration.verbose = false
|
2
|
+
ActiveRecord::Schema.define(:version => 1) do
|
3
|
+
|
4
|
+
create_table :attachments, :force => true do |t|
|
5
|
+
t.string :filename
|
6
|
+
t.string :content_type
|
7
|
+
t.string :attachable_type
|
8
|
+
t.integer :attachable_id
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :documents, :force => true do |t|
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
TEST_ROOT_DIR = File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
frameworks = %w(activesupport activerecord actionpack)
|
4
|
+
|
5
|
+
rails = [
|
6
|
+
File.expand_path('../../../rails', TEST_ROOT_DIR),
|
7
|
+
File.expand_path('../../rails', TEST_ROOT_DIR)
|
8
|
+
].detect do |possible_rails|
|
9
|
+
begin
|
10
|
+
entries = Dir.entries(possible_rails)
|
11
|
+
frameworks.all? { |framework| entries.include?(framework) }
|
12
|
+
rescue Errno::ENOENT
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
raise "Couldn't find Rails directory!" unless rails
|
18
|
+
|
19
|
+
frameworks.each { |framework| $:.unshift(File.join(rails, framework, 'lib')) }
|
20
|
+
$:.unshift File.join(TEST_ROOT_DIR, '/../lib')
|
21
|
+
$:.unshift TEST_ROOT_DIR
|
22
|
+
|
23
|
+
ENV['RAILS_ENV'] = 'test'
|
24
|
+
|
25
|
+
# Rails libs
|
26
|
+
begin
|
27
|
+
require 'active_support'
|
28
|
+
require 'active_record'
|
29
|
+
require 'action_controller'
|
30
|
+
rescue LoadError
|
31
|
+
raise "Please install Attachment-San as Rails plugin before running the tests."
|
32
|
+
end
|
33
|
+
|
34
|
+
require "pathname"
|
35
|
+
module Rails
|
36
|
+
def self.env
|
37
|
+
'test'
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.root
|
41
|
+
Pathname.new('/path/to/app')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
require File.expand_path('../../rails/init', __FILE__)
|
46
|
+
|
47
|
+
# Libraries for testing
|
48
|
+
require 'rubygems' rescue LoadError
|
49
|
+
require 'sqlite3'
|
50
|
+
require 'test/spec'
|
51
|
+
require 'mocha'
|
52
|
+
require 'fileutils'
|
53
|
+
|
54
|
+
logdir = File.join(TEST_ROOT_DIR, 'log')
|
55
|
+
FileUtils.mkdir_p(logdir)
|
56
|
+
ActiveRecord::Base.logger = Logger.new File.join(logdir, 'test.log')
|
57
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ":memory:")
|
58
|
+
|
59
|
+
TMP_DIR = File.join(TEST_ROOT_DIR, 'tmp')
|
60
|
+
|
61
|
+
# Classes and methods to aid testing
|
62
|
+
require 'lib/schema'
|
63
|
+
|
64
|
+
require 'fixtures/models/attachment'
|
65
|
+
require 'fixtures/models/document'
|
66
|
+
require 'fixtures/models/options_stub'
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "AttachmentSan's variant class methods" do
|
4
|
+
it "should return the variant class to use" do
|
5
|
+
reflection = Logo.reflect_on_variant(:header)
|
6
|
+
reflection[:class].should.be MyVariant
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should by default use the AttachmentSan::Variant class" do
|
10
|
+
reflection = Image.reflect_on_variant(:thumbnail)
|
11
|
+
reflection[:class].should.be AttachmentSan::Variant
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not define a variant twice" do
|
15
|
+
count_before = Logo.variant_reflections.length
|
16
|
+
Document.has_attachment :logo, :variants => { :header => MyVariant }
|
17
|
+
Logo.variant_reflections.length.should == count_before
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "A AttachmentSan::Variant instance in general" do
|
22
|
+
before do
|
23
|
+
@upload = rails_icon
|
24
|
+
@document = Document.new
|
25
|
+
|
26
|
+
@image = @document.images.build(:uploaded_file => @upload)
|
27
|
+
@thumbnail = @image.thumbnail
|
28
|
+
@medium_sized = @image.medium_sized
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return the record it is a variant of" do
|
32
|
+
@image.original.record.should == @image
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should return the `original' variant" do
|
36
|
+
@image.original.should == @image.original
|
37
|
+
@thumbnail.original.should == @image.original
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should call the process proc" do
|
41
|
+
MyProcessor.expects(:new).with(@thumbnail)
|
42
|
+
@thumbnail.process!
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should return the base_path specified in the attachment_san_options" do
|
46
|
+
@thumbnail.base_path.should == Attachment.attachment_san_options[:base_path]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return the public_base_path specified in the attachment_san_options" do
|
50
|
+
@thumbnail.public_base_path.should == Attachment.attachment_san_options[:public_base_path]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return the extension of the original file" do
|
54
|
+
Image.attachment_san_options[:extension] = :keep_original
|
55
|
+
@thumbnail.extension.should == 'png'
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should use the extension specified in the attachment_san_options" do
|
59
|
+
Image.attachment_san_options[:extension] = :jpeg
|
60
|
+
@thumbnail.extension.should == :jpeg
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should create the directory that the file_path returns" do
|
64
|
+
Image.attachment_san_options[:filename_scheme] = :record_identifier
|
65
|
+
|
66
|
+
@thumbnail.dir_path.should == File.dirname(@thumbnail.file_path)
|
67
|
+
File.should.not.exist @thumbnail.dir_path
|
68
|
+
@thumbnail.mkdir!
|
69
|
+
File.should.exist @thumbnail.dir_path
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should return a file_path without trailing dot if the original filename has no extension" do
|
73
|
+
variant = @document.misc_files.build(
|
74
|
+
:uploaded_file => uploaded_file(File.expand_path('../../Rakefile', __FILE__), 'text/plain')
|
75
|
+
).original
|
76
|
+
|
77
|
+
variant.file_path.should == File.join(variant.base_path, 'Rakefile')
|
78
|
+
end
|
79
|
+
|
80
|
+
{ :keep_original => 'png', :jpg => 'jpg' }.each do |setting, ext|
|
81
|
+
it "should return a filename named after the variant name plus #{setting}" do
|
82
|
+
Image.attachment_san_options[:filename_scheme] = :variant_name
|
83
|
+
Image.attachment_san_options[:extension] = setting
|
84
|
+
|
85
|
+
@thumbnail.filename.should == "thumbnail.#{ext}"
|
86
|
+
@thumbnail.file_path.should == File.join(@thumbnail.base_path, "thumbnail.#{ext}")
|
87
|
+
@thumbnail.public_path.should == File.join(@thumbnail.public_base_path, "thumbnail.#{ext}")
|
88
|
+
|
89
|
+
@medium_sized.filename.should == "medium_sized.#{ext}"
|
90
|
+
@medium_sized.file_path.should == File.join(@thumbnail.base_path, "medium_sized.#{ext}")
|
91
|
+
@medium_sized.public_path.should == File.join(@thumbnail.public_base_path, "medium_sized.#{ext}")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should return a filename which should be a random token from the record and append #{setting}" do
|
95
|
+
Image.attachment_san_options[:filename_scheme] = :token
|
96
|
+
Image.attachment_san_options[:extension] = setting
|
97
|
+
@image.stubs(:token).returns('556d2e8e')
|
98
|
+
|
99
|
+
@image.original.filename.should == "55/6d/2e/8e/rails.png"
|
100
|
+
@image.original.file_path.should == File.join(@thumbnail.base_path, "55/6d/2e/8e/rails.png")
|
101
|
+
@image.original.public_path.should == File.join(@thumbnail.public_base_path, "55/6d/2e/8e/rails.png")
|
102
|
+
|
103
|
+
@thumbnail.filename.should == "55/6d/2e/8e/rails.thumbnail.#{ext}"
|
104
|
+
@thumbnail.file_path.should == File.join(@thumbnail.base_path, "55/6d/2e/8e/rails.thumbnail.#{ext}")
|
105
|
+
@thumbnail.public_path.should == File.join(@thumbnail.public_base_path, "55/6d/2e/8e/rails.thumbnail.#{ext}")
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should return a filename which is based on the record identifier and variant name plus #{setting}" do
|
109
|
+
Attachment.reset!
|
110
|
+
|
111
|
+
Image.attachment_san_options[:extension] = setting
|
112
|
+
Image.attachment_san_options[:filename_scheme] = :record_identifier
|
113
|
+
@image.save!
|
114
|
+
|
115
|
+
@thumbnail.filename.should == "/images/#{@image.id}/thumbnail.#{ext}"
|
116
|
+
@thumbnail.file_path.should == File.join(@thumbnail.base_path, "/images/#{@image.id}/thumbnail.#{ext}")
|
117
|
+
@thumbnail.public_path.should == File.join(@thumbnail.public_base_path, "/images/#{@image.id}/thumbnail.#{ext}")
|
118
|
+
|
119
|
+
@medium_sized.filename.should == "/images/#{@image.id}/medium_sized.#{ext}"
|
120
|
+
@medium_sized.file_path.should == File.join(@thumbnail.base_path, "/images/#{@image.id}/medium_sized.#{ext}")
|
121
|
+
@medium_sized.public_path.should == File.join(@thumbnail.public_base_path, "/images/#{@image.id}/medium_sized.#{ext}")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "AttachmentSan::Variant, concerning a default variant without extra options" do
|
127
|
+
before do
|
128
|
+
@upload = rails_icon
|
129
|
+
@document = Document.new
|
130
|
+
@document.build_watermark :uploaded_file => @upload
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should use a custom variant for an `original'" do
|
134
|
+
@document.watermark.original.should.be.instance_of AttachmentSan::Variant::Original
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should simply copy the file to the file_path" do
|
138
|
+
Attachment.reset!
|
139
|
+
|
140
|
+
file_path = @document.watermark.original.file_path
|
141
|
+
File.should.not.exist file_path
|
142
|
+
@document.save!
|
143
|
+
|
144
|
+
File.should.exist file_path
|
145
|
+
File.read(file_path).should == File.read(@upload.path)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "AttachmentSan::Variant, concerning a default variant with extra options" do
|
150
|
+
before do
|
151
|
+
@upload = rails_icon
|
152
|
+
@document = Document.new
|
153
|
+
@variant = @document.misc_files.build(:uploaded_file => @upload).original
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should return the filename_scheme value that was specified" do
|
157
|
+
@variant.filename_scheme.should == :keep_original end
|
158
|
+
|
159
|
+
it "should return the original file's name" do
|
160
|
+
@variant.filename.should == @variant.record.filename
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should call the specified process proc" do
|
164
|
+
result = @variant.process!
|
165
|
+
result.should == :from_process_proc
|
166
|
+
end
|
167
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: attachment-san
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eloy Duran
|
8
|
+
- Manfred Stienstra
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-11-19 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Rails plugin for easy and rich attachment manipulation.
|
18
|
+
email:
|
19
|
+
- eloy@fngtps.com
|
20
|
+
- manfred@fngtps.com
|
21
|
+
executables: []
|
22
|
+
|
23
|
+
extensions: []
|
24
|
+
|
25
|
+
extra_rdoc_files:
|
26
|
+
- LICENSE
|
27
|
+
- README
|
28
|
+
files:
|
29
|
+
- LICENSE
|
30
|
+
- README
|
31
|
+
- Rakefile
|
32
|
+
- TODO
|
33
|
+
- VERSION
|
34
|
+
- attachment-san.gemspec
|
35
|
+
- examples/photo.rb
|
36
|
+
- examples/photos_controller.rb
|
37
|
+
- examples/post.rb
|
38
|
+
- lib/attachment_san.rb
|
39
|
+
- lib/attachment_san/has.rb
|
40
|
+
- lib/attachment_san/test/helper.rb
|
41
|
+
- lib/attachment_san/test/rails.png
|
42
|
+
- lib/attachment_san/variant.rb
|
43
|
+
- napkin.rb
|
44
|
+
- rails/init.rb
|
45
|
+
- test/attachment_san_test.rb
|
46
|
+
- test/fixtures/models/attachment.rb
|
47
|
+
- test/fixtures/models/document.rb
|
48
|
+
- test/fixtures/models/options_stub.rb
|
49
|
+
- test/has_test.rb
|
50
|
+
- test/lib/schema.rb
|
51
|
+
- test/test_helper.rb
|
52
|
+
- test/variant_test.rb
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://github.com/Fingertips/attachment-san
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options:
|
59
|
+
- --charset=UTF-8
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.3.5
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Rails plugin for easy and rich attachment manipulation.
|
81
|
+
test_files:
|
82
|
+
- test/attachment_san_test.rb
|
83
|
+
- test/fixtures/models/attachment.rb
|
84
|
+
- test/fixtures/models/document.rb
|
85
|
+
- test/fixtures/models/options_stub.rb
|
86
|
+
- test/has_test.rb
|
87
|
+
- test/lib/schema.rb
|
88
|
+
- test/test_helper.rb
|
89
|
+
- test/variant_test.rb
|
90
|
+
- examples/photo.rb
|
91
|
+
- examples/photos_controller.rb
|
92
|
+
- examples/post.rb
|