paperdragon 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +54 -12
- data/Rakefile +7 -0
- data/lib/paperdragon.rb +9 -1
- data/lib/paperdragon/attachment.rb +80 -0
- data/lib/paperdragon/file.rb +46 -0
- data/lib/paperdragon/file/operations.rb +68 -0
- data/lib/paperdragon/metadata.rb +31 -0
- data/lib/paperdragon/model.rb +23 -0
- data/lib/paperdragon/paperclip.rb +68 -0
- data/lib/paperdragon/paperclip/model.rb +40 -0
- data/lib/paperdragon/task.rb +51 -0
- data/lib/paperdragon/version.rb +1 -1
- data/paperdragon.gemspec +6 -2
- data/test/attachment_test.rb +91 -0
- data/test/file_test.rb +138 -0
- data/test/fixtures/apotomo.png +0 -0
- data/test/metadata_test.rb +54 -0
- data/test/model_test.rb +35 -0
- data/test/paperclip_uid_test.rb +57 -0
- data/test/task_test.rb +105 -0
- data/test/test_helper.rb +12 -0
- metadata +71 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f827f261f58ce5b096178c2b90010e90a99b8e27
|
4
|
+
data.tar.gz: f6731a32d9779faab6b1f83da6df1006acab51a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4481129325b2fa60b3034ae07e1cb4a9e636996f68fc4697c03356863e1e25136f272ae925c12e37ac09b7bff94ebd509d548fa07783df38c957a54e587234ee
|
7
|
+
data.tar.gz: a39824b4503ef45240a09a80845457194504b954cfe66ee4a865207589f5000be7d57dccbceddded4b328b6c2fb0cfaa387e62974999d2c3e8b7f465155a3866
|
data/README.md
CHANGED
@@ -8,22 +8,64 @@ Add this line to your application's Gemfile:
|
|
8
8
|
|
9
9
|
gem 'paperdragon'
|
10
10
|
|
11
|
-
And then execute:
|
12
11
|
|
13
|
-
|
12
|
+
Paperdragon is completely decoupled from ActiveRecord. Attachment-related calls are delegated to paperdragon objects, the model is solely used for persisting file UIDs.
|
14
13
|
|
15
|
-
|
14
|
+
Where Paperclip or Carrierwave offer you a handy DSL to configure the processing, Paperdragon comes with an API. You _program_ what you wanna do. This is only a tiny little bit more code and gives you complete control over the entire task.
|
16
15
|
|
17
|
-
$ gem install paperdragon
|
18
16
|
|
19
|
-
## Usage
|
20
17
|
|
21
|
-
|
18
|
+
The goal is to make you _understand_ what is going on.
|
22
19
|
|
23
|
-
|
20
|
+
* you control processing and storage, e.g. first thumbnails and cropping, then process the rest. easy to sidekiq.
|
21
|
+
error handling
|
22
|
+
UID generation handled by you. also, updating (e.g. new fingerprint)
|
23
|
+
* only process subset, e.g. in test.
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
|
26
|
+
File
|
27
|
+
|
28
|
+
All process methods return Metadata hash
|
29
|
+
yield Job, save it from the block if you need it
|
30
|
+
override #default_metadata_for when you wanna change it
|
31
|
+
last arg in process method gets merged into metadata hash
|
32
|
+
|
33
|
+
Design
|
34
|
+
Operations in File look like scripts per design. I could have abstracted various steps into higher level methods, however, as file processing _is_ a lot scripting, I decided to sacrifice redundancy for better understandable code.
|
35
|
+
|
36
|
+
|
37
|
+
Paperclip Compatibility
|
38
|
+
|
39
|
+
1. Stores file to same location as paperclip would do.
|
40
|
+
2. `Photo#url` will return the same URL as paperclip.
|
41
|
+
3. P::Model image.url(:thumb) still works, your rendering code will still work.
|
42
|
+
4. Cleaner API for generating URLs. For example, we needed to copy images from production to staging. With paperclip, it was impossible to create paths for both environments.
|
43
|
+
|
44
|
+
Paperclip uses several columns to compute the UID. Once this is done, it doesn't store that UID in the database but updates the respective fields, which makes it a bit awkward to maintain.
|
45
|
+
|
46
|
+
Paperdragon simply dumps the image uid along with meta data into image_meta_data.
|
47
|
+
|
48
|
+
You have to take care of updating image_fingerprint etc yourself when changing stuff and still using paperclip to compute urls.
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
Original paperclip UID:
|
54
|
+
it { pic.image(:original).should == "/system/test/pics/images/002/216/376/bc7b26d983db8ced792e38f0c34aba417f75c2e7_key/original-c5b7e624adc5b67e13435baf26e65bc8-1399980114/DSC_4876.jpg" }
|
55
|
+
|
56
|
+
1) Uid
|
57
|
+
Failure/Error: should == "system/test/pics/images/002/216/376/bc7b26d983db8ced792e38f0c34aba417f75c2e7_key/original-c5b7e624adc5b67e13435baf26e65bc8-1399980114/DSC_4876.jpg" }
|
58
|
+
expected: "system/test/pics/images/002/216/376/bc7b26d983db8ced792e38f0c34aba417f75c2e7_key/original-c5b7e624adc5b67e13435baf26e65bc8-1399980114/DSC_4876.jpg"
|
59
|
+
got: "system/test/pics/images/002/216/376/bc7b26d983db8ced792e38f0c34aba417f75c2e7_key/original/DSC_4876.jpg" (using ==)
|
60
|
+
|
61
|
+
Feel like a hacker reverse-engineering
|
62
|
+
|
63
|
+
## Rails
|
64
|
+
|
65
|
+
Dragonfly.app.configure do
|
66
|
+
plugin :imagemagick
|
67
|
+
|
68
|
+
datastore :file,
|
69
|
+
:server_root => 'public',
|
70
|
+
:root_path => 'public/images'
|
71
|
+
end
|
data/Rakefile
CHANGED
data/lib/paperdragon.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
require "paperdragon/version"
|
2
|
+
require 'dragonfly'
|
2
3
|
|
3
4
|
module Paperdragon
|
4
|
-
|
5
|
+
class MissingUploadError < RuntimeError
|
6
|
+
end
|
5
7
|
end
|
8
|
+
|
9
|
+
require 'paperdragon/file'
|
10
|
+
require 'paperdragon/metadata'
|
11
|
+
require 'paperdragon/attachment'
|
12
|
+
require 'paperdragon/task'
|
13
|
+
require 'paperdragon/model'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'uber/inheritable_attr'
|
2
|
+
|
3
|
+
module Paperdragon
|
4
|
+
# Override #build_uid / #rebuild_uid.
|
5
|
+
# Note that we only encode the UID when computing it the first time. It is then stored encoded
|
6
|
+
# and no escaping happens at any time after that.
|
7
|
+
# You may use options.
|
8
|
+
# only saves metadata, does not know about model.
|
9
|
+
# Attachment is a builder for File and knows about metadata. It is responsible for creating UID if metadata is empty.
|
10
|
+
class Attachment
|
11
|
+
extend Uber::InheritableAttr
|
12
|
+
inheritable_attr :file_class #, strategy: ->{ tap{} }
|
13
|
+
self.file_class = ::Paperdragon::File # default value. # !!! be careful, this gets cloned in subclasses and thereby becomes a subclass of PD:File.
|
14
|
+
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def initialize(metadata, options={})
|
18
|
+
@metadata = Metadata.new(metadata)
|
19
|
+
@options = options # to be used in #(re)build_uid for your convenience. # DISCUSS: we pass in the model here - is that what we want?
|
20
|
+
end
|
21
|
+
attr_reader :metadata # TODO: test me.
|
22
|
+
|
23
|
+
def [](style, file=nil) # not sure if i like passing file here, consider this method signature semi-public.
|
24
|
+
file_metadata = @metadata[style]
|
25
|
+
|
26
|
+
uid = file_metadata[:uid] || uid_from(style, file)
|
27
|
+
self.class.file_class.new(uid, file_metadata)
|
28
|
+
end
|
29
|
+
|
30
|
+
# DSL method providing the task instance.
|
31
|
+
# When called with block, it yields the task and returns the generated metadata.
|
32
|
+
def task(upload=nil, &block)
|
33
|
+
task = Task.new(self, upload, &block)
|
34
|
+
|
35
|
+
return task unless block_given?
|
36
|
+
task.metadata_hash
|
37
|
+
end
|
38
|
+
|
39
|
+
def rebuild_uid(file, fingerprint=nil) # the signature of this method is to be considered semi-private.
|
40
|
+
ext = ::File.extname(file.uid)
|
41
|
+
name = ::File.basename(file.uid, ext)
|
42
|
+
file.uid.sub(name, "#{name}#{fingerprint}")
|
43
|
+
end
|
44
|
+
|
45
|
+
def exists? # should be #uploaded? or #stored?
|
46
|
+
# not sure if i like that kind of state here, so consider method semi-public.
|
47
|
+
@metadata.populated?
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
attr_reader :options
|
52
|
+
|
53
|
+
# Computes UID when File doesn't have one, yet. Called in #initialize.
|
54
|
+
def uid_from(*args)
|
55
|
+
build_uid(*args)
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_uid(style, file)
|
59
|
+
# can we use Dragonfly's API here?
|
60
|
+
"#{style}-#{Dragonfly::TempObject.new(file).original_filename}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
module SanitizeUid
|
66
|
+
def uid_from(*args)
|
67
|
+
sanitize(super)
|
68
|
+
end
|
69
|
+
|
70
|
+
def sanitize(uid)
|
71
|
+
#URI::encode(uid) # this is wrong, we can't send %21 in path to S3!
|
72
|
+
uid.gsub(/(#|\?)/, "_") # escape # and ?, only.
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
include InstanceMethods
|
78
|
+
include SanitizeUid # overrides #uid_from.
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Paperdragon
|
2
|
+
# A physical file with a UID.
|
3
|
+
class File
|
4
|
+
def initialize(uid, options={})
|
5
|
+
@uid = uid
|
6
|
+
@options = options
|
7
|
+
@data = nil # DISCUSS: do we need that here?
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :uid, :options
|
11
|
+
alias_method :metadata, :options
|
12
|
+
|
13
|
+
def url(opts={})
|
14
|
+
Dragonfly.app.remote_url_for(uid, opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def data
|
18
|
+
puts "........................FETCH (data): #{uid}, #{@data ? :cached : (:fetching)}"
|
19
|
+
@data ||= Dragonfly.app.fetch(uid).data
|
20
|
+
end
|
21
|
+
|
22
|
+
# attr_reader :meta_data
|
23
|
+
|
24
|
+
require 'paperdragon/file/operations'
|
25
|
+
include Process
|
26
|
+
include Delete
|
27
|
+
include Reprocess
|
28
|
+
include Rename
|
29
|
+
|
30
|
+
|
31
|
+
private
|
32
|
+
# replaces the UID.
|
33
|
+
def uid!(new_uid)
|
34
|
+
@uid = new_uid
|
35
|
+
end
|
36
|
+
|
37
|
+
# Override if you want to include/exclude properties in this file metadata.
|
38
|
+
def default_metadata_for(job)
|
39
|
+
{:width => job.width, :height => job.height, :uid => uid}#, :content_type => job.mime_type}
|
40
|
+
end
|
41
|
+
|
42
|
+
def metadata_for(job, additional={})
|
43
|
+
default_metadata_for(job).merge(additional)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Paperdragon
|
2
|
+
class File
|
3
|
+
# DISCUSS: allow the metadata passing here or not?
|
4
|
+
module Process
|
5
|
+
def process!(file, metadata={})
|
6
|
+
job = Dragonfly.app.new_job(file)
|
7
|
+
|
8
|
+
yield job if block_given?
|
9
|
+
|
10
|
+
puts "........................STORE (process): #{uid}"
|
11
|
+
job.store(path: uid, :headers => {'x-amz-acl' => 'public-read', "Content-Type" => "image/jpeg"})
|
12
|
+
|
13
|
+
@data = nil
|
14
|
+
metadata_for(job, metadata)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
module Delete
|
20
|
+
def delete!
|
21
|
+
puts "........................DELETE (delete): #{uid}"
|
22
|
+
Dragonfly.app.destroy(uid)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
module Reprocess
|
28
|
+
def reprocess!(fingerprint, original, metadata={})
|
29
|
+
job = Dragonfly.app.new_job(original.data) # inheritance here somehow?
|
30
|
+
|
31
|
+
yield job if block_given?
|
32
|
+
|
33
|
+
old_uid = uid
|
34
|
+
uid!(fingerprint) # new UID is computed and set.
|
35
|
+
|
36
|
+
puts "........................STORE (reprocess): #{uid}"
|
37
|
+
job.store(path: uid, headers: {'x-amz-acl' => 'public-read', "Content-Type" => "image/jpeg"}) # store with thumb url.
|
38
|
+
|
39
|
+
puts "........................DELETE (reprocess): #{old_uid}"
|
40
|
+
Dragonfly.app.destroy(old_uid)
|
41
|
+
|
42
|
+
@data = nil
|
43
|
+
metadata_for(job, metadata)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
module Rename
|
49
|
+
def rename!(fingerprint, metadata={}) # fixme: we are currently ignoring the custom metadata.
|
50
|
+
old_uid = uid
|
51
|
+
uid!(fingerprint)
|
52
|
+
|
53
|
+
puts "........................MV:
|
54
|
+
#{old_uid}
|
55
|
+
#{uid}"
|
56
|
+
# dragonfly_s3 = Dragonfly.app.datastore
|
57
|
+
# Dragonfly.app.datastore.storage.copy_object(dragonfly_s3.bucket_name, old_uid, dragonfly_s3.bucket_name, uid, {'x-amz-acl' => 'public-read', "Content-Type" => "image/jpeg"})
|
58
|
+
yield old_uid, uid
|
59
|
+
|
60
|
+
puts "........................DELETE: #{old_uid}"
|
61
|
+
Dragonfly.app.destroy(old_uid)
|
62
|
+
|
63
|
+
|
64
|
+
self.metadata.merge(:uid => uid) # usually, metadata is already set to the old metadata when File was created via Attachment.
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Paperdragon
|
2
|
+
# 2-level meta data hash for a file. Returns empty string if not found.
|
3
|
+
# Metadata.new(nil)[:original][:width] => ""
|
4
|
+
# Holds metadata for an attachment. This is a hash keyed by versions, e.g. +:original+,
|
5
|
+
# +:thumb+, and so on.
|
6
|
+
class Metadata
|
7
|
+
def initialize(hash)
|
8
|
+
@hash = hash || {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](name)
|
12
|
+
@hash[name] || {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def populated?
|
16
|
+
@hash.size > 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def merge!(hash)
|
20
|
+
@hash.merge!(hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dup
|
24
|
+
self.class.new(@hash.dup)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_hash
|
28
|
+
@hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Paperdragon
|
2
|
+
module Model
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def processable(name, attachment_class=Attachment)
|
9
|
+
include attachment_accessor_for(name, attachment_class)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
# Creates Avatar#image that returns a Paperdragon::File instance.
|
14
|
+
def attachment_accessor_for(name, attachment_class)
|
15
|
+
mod = Module.new do # TODO: abstract that into Uber, we use it everywhere.
|
16
|
+
define_method name do
|
17
|
+
attachment_class.new(self.image_meta_data, {:model => self})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Paperdragon
|
2
|
+
class Paperclip
|
3
|
+
|
4
|
+
# Compute a UID to be compatible with paperclip. This class is meant to be subclassed so you can write
|
5
|
+
# your specific file path.
|
6
|
+
# Immutable
|
7
|
+
class Uid
|
8
|
+
def self.from(options)
|
9
|
+
new(options).call
|
10
|
+
end
|
11
|
+
|
12
|
+
# "/system/:class/:attachment/:id_partition/:style/:filename"
|
13
|
+
def initialize(options)
|
14
|
+
@class_name = options[:class_name]
|
15
|
+
@attachment = options[:attachment]
|
16
|
+
@id = options[:id]
|
17
|
+
@style = options[:style]
|
18
|
+
@updated_at = options[:updated_at]
|
19
|
+
@file_name = options[:file_name]
|
20
|
+
@hash_secret = options[:hash_secret]
|
21
|
+
@fingerprint = options[:fingerprint] # not used in default.
|
22
|
+
end
|
23
|
+
|
24
|
+
def call
|
25
|
+
# default:
|
26
|
+
# system/:class/:attachment/:id_partition/:style/:filename
|
27
|
+
"#{root}/#{class_name}/#{attachment}/#{id_partition}/#{hash}/#{style}/#{file_name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
attr_reader :class_name, :attachment, :id, :style, :file_name, :hash_secret, :updated_at, :fingerprint
|
32
|
+
|
33
|
+
def root
|
34
|
+
"system"
|
35
|
+
end
|
36
|
+
|
37
|
+
def id_partition
|
38
|
+
IdPartition.call(id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def hash
|
42
|
+
HashKey.call(hash_secret, class_name, attachment, id, style, updated_at)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
class IdPartition
|
47
|
+
def self.call(id)
|
48
|
+
("%09d" % id).scan(/\d{3}/).join("/") # FIXME: only works with integers.
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# ":class/:attachment/:id/:style/:updated_at"
|
54
|
+
class HashKey
|
55
|
+
require 'openssl' unless defined?(OpenSSL)
|
56
|
+
|
57
|
+
# cover_girls/images/4841/thumb/1402617353
|
58
|
+
def self.call(secret, class_name, attachment, id, style, updated_at)
|
59
|
+
data = "#{class_name}/#{attachment}/#{id}/#{style}/#{updated_at}"
|
60
|
+
# puts "[Paperdragon] HashKey <--------------------- #{data}"
|
61
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
require 'paperdragon/paperclip/model'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Paperdragon
|
2
|
+
class Paperclip
|
3
|
+
# DISCUSS: I want to remove this module.
|
4
|
+
module Model
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def processable(name, attachment_class)
|
11
|
+
# this overrides #image (or whatever the name is) from Paperclip::Model::processable.
|
12
|
+
# This allows using both paperclip's `image.url(:thumb)` and the new paperdragon style
|
13
|
+
# `image(:thumb).url`.
|
14
|
+
mod = Module.new do # TODO: merge with attachment_accessor_for.
|
15
|
+
define_method name do # e.g. Avatar#image
|
16
|
+
Proxy.new(self, attachment_class) # provide paperclip DSL.
|
17
|
+
end
|
18
|
+
end
|
19
|
+
include mod
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# Needed to expose Paperclip's DSL, like avatar.image.url(:thumb).
|
25
|
+
class Proxy
|
26
|
+
def initialize(model, attachment_class)
|
27
|
+
@attachment = attachment_class.new(model.image_meta_data, {:model => model})
|
28
|
+
end
|
29
|
+
|
30
|
+
def url(style)
|
31
|
+
@attachment[style].url # Avatar::Photo.new(avatar, :thumb).url
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(name, *args)
|
35
|
+
@attachment.send(name, *args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Paperdragon
|
2
|
+
# Gives a simple API for processing multiple versions of a single attachment.
|
3
|
+
class Task
|
4
|
+
def initialize(attachment, upload=nil)
|
5
|
+
@attachment = attachment
|
6
|
+
@upload = upload
|
7
|
+
@metadata = attachment.metadata.dup # DISCUSS: keep this dependency?
|
8
|
+
|
9
|
+
yield self if block_given?
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :metadata
|
13
|
+
def metadata_hash # semi-private, might be removed.
|
14
|
+
metadata.to_hash
|
15
|
+
end
|
16
|
+
|
17
|
+
# process!(style, [*args,] &block) :
|
18
|
+
# version = CoverGirl::Photo.new(@model, style, *args)
|
19
|
+
# metadata = version.process!(upload, &block)
|
20
|
+
# merge! {style => metadata}
|
21
|
+
def process!(style, &block)
|
22
|
+
@metadata.merge!(style => file(style, upload).process!(upload, &block))
|
23
|
+
end
|
24
|
+
|
25
|
+
# fingerprint optional => filename is gonna remain the same
|
26
|
+
# original nil => use [:original]
|
27
|
+
def reprocess!(style, fingerprint=nil, original=nil, &block)
|
28
|
+
original ||= file(:original)
|
29
|
+
version = file(style)
|
30
|
+
new_uid = @attachment.rebuild_uid(version, fingerprint)
|
31
|
+
|
32
|
+
@metadata.merge!(style => version.reprocess!(new_uid, original, &block))
|
33
|
+
end
|
34
|
+
|
35
|
+
def rename!(style, fingerprint, &block)
|
36
|
+
version = file(style)
|
37
|
+
new_uid = @attachment.rebuild_uid(version, fingerprint)
|
38
|
+
|
39
|
+
@metadata.merge!(style => version.rename!(new_uid, &block))
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def file(style, upload=nil)
|
44
|
+
@attachment[style, upload]
|
45
|
+
end
|
46
|
+
|
47
|
+
def upload
|
48
|
+
@upload or raise MissingUploadError.new("You called #process! but didn't pass an uploaded file to Attachment#task.")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/paperdragon/version.rb
CHANGED
data/paperdragon.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Paperdragon::VERSION
|
9
9
|
spec.authors = ["Nick Sutterer"]
|
10
10
|
spec.email = ["apotonick@gmail.com"]
|
11
|
-
spec.summary = %q{
|
12
|
-
spec.description = %q{
|
11
|
+
spec.summary = %q{Explicit image processing based on Dragonfly with Paperclip compatibility.}
|
12
|
+
spec.description = %q{Explicit image processing based on Dragonfly with Paperclip compatibility.}
|
13
13
|
spec.homepage = ""
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -18,6 +18,10 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency "dragonfly"
|
22
|
+
spec.add_dependency "uber"
|
23
|
+
|
21
24
|
spec.add_development_dependency "bundler", "~> 1.6"
|
22
25
|
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "minitest"
|
23
27
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class AttachmentSpec < MiniTest::Spec
|
4
|
+
class Attachment < Paperdragon::Attachment
|
5
|
+
private
|
6
|
+
def uid_from(style)
|
7
|
+
"/uid/#{style}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "existing" do
|
12
|
+
subject { Attachment.new({:original => {:uid=>"/uid/1234.jpg", :width => 99}}) }
|
13
|
+
|
14
|
+
it { subject[:original].uid.must_equal "/uid/1234.jpg" }
|
15
|
+
it { subject[:original].options.must_equal({:uid=>"/uid/1234.jpg", :width => 99}) }
|
16
|
+
it { subject.exists?.must_equal true }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "new" do
|
20
|
+
subject { Attachment.new(nil) }
|
21
|
+
|
22
|
+
it { subject[:original].uid.must_equal "/uid/original" }
|
23
|
+
it { subject[:original].options.must_equal({}) }
|
24
|
+
it { subject.exists?.must_equal false }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "new with empty metadata hash" do
|
28
|
+
subject { Attachment.new({}) }
|
29
|
+
|
30
|
+
it { subject[:original].uid.must_equal "/uid/original" }
|
31
|
+
it { subject[:original].options.must_equal({}) }
|
32
|
+
it { subject.exists?.must_equal false }
|
33
|
+
end
|
34
|
+
|
35
|
+
# #metadata
|
36
|
+
it { Attachment.new({}).metadata.to_hash.must_equal( {}) }
|
37
|
+
|
38
|
+
|
39
|
+
# test passing options into Attachment and use that in #build_uid.
|
40
|
+
class AttachmentUsingOptions < Paperdragon::Attachment
|
41
|
+
private
|
42
|
+
def build_uid(style, file)
|
43
|
+
"uid/#{style}/#{options[:filename]}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# use in new --> build_uid.
|
48
|
+
it { AttachmentUsingOptions.new(nil, {:filename => "apotomo.png"})[:original].uid.must_equal "uid/original/apotomo.png" }
|
49
|
+
|
50
|
+
|
51
|
+
# test using custom File class in Attachment.
|
52
|
+
class OverridingAttachment < Paperdragon::Attachment
|
53
|
+
class File < Paperdragon::File
|
54
|
+
def uid
|
55
|
+
"from/file"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
self.file_class= File
|
59
|
+
end
|
60
|
+
|
61
|
+
it { OverridingAttachment.new(nil)[:original, Pathname.new("not-considered.JPEG")].uid.must_equal "from/file" }
|
62
|
+
|
63
|
+
|
64
|
+
# test UID sanitising. this happens only when computing the UID with a new attachment!
|
65
|
+
describe "insane filename" do
|
66
|
+
it { AttachmentUsingOptions.new(nil, {:filename => "(use)? apotomo #1#.png"})[:original].uid.must_equal "uid/original/(use)_ apotomo _1_.png" }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
class AttachmentModelSpec < MiniTest::Spec
|
72
|
+
class Attachment < Paperdragon::Attachment
|
73
|
+
private
|
74
|
+
def build_uid(style, file)
|
75
|
+
"#{options[:model].class}/uid/#{style}/#{options[:filename]}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "existing" do
|
80
|
+
let (:existing) { OpenStruct.new(:image_meta_data => {:original => {:uid=>"/uid/1234.jpg"}}) }
|
81
|
+
subject { Attachment.new(existing.image_meta_data, :model => existing) }
|
82
|
+
|
83
|
+
it { subject[:original].uid.must_equal "/uid/1234.jpg" } # notice that #uid_from is not called.
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "new" do
|
87
|
+
subject { Attachment.new(nil, :filename => "apotomo.png", :model => OpenStruct.new) } # you can pass options into Attachment::new that may be used in #build_uid
|
88
|
+
|
89
|
+
it { subject[:original].uid.must_equal "OpenStruct/uid/original/apotomo.png" }
|
90
|
+
end
|
91
|
+
end
|
data/test/file_test.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
Dragonfly.app.configure do
|
4
|
+
plugin :imagemagick
|
5
|
+
|
6
|
+
#url_host 'http://some.domain.com:4000'
|
7
|
+
|
8
|
+
datastore :file,
|
9
|
+
:server_root => 'public',
|
10
|
+
:root_path => 'public/paperdragon'
|
11
|
+
end
|
12
|
+
|
13
|
+
class PaperdragonFileTest < MiniTest::Spec
|
14
|
+
it { Paperdragon::File.new("123").uid.must_equal "123" }
|
15
|
+
it { Paperdragon::File.new("123").url.must_equal "/paperdragon/123" } # FIXME: how to add host?
|
16
|
+
it { Paperdragon::File.new("123") }
|
17
|
+
|
18
|
+
describe "#metadata" do
|
19
|
+
it { Paperdragon::File.new("123").metadata.must_equal({}) }
|
20
|
+
it { Paperdragon::File.new("123", :width => 16).metadata.must_equal({:width => 16}) }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#data" do
|
24
|
+
it do
|
25
|
+
Paperdragon::File.new(uid).process!(logo)
|
26
|
+
Paperdragon::File.new(uid).data.size.must_equal 9632
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
let (:logo) { Pathname("test/fixtures/apotomo.png") }
|
31
|
+
|
32
|
+
# process! saves file
|
33
|
+
# TODO: remote storage, server root, etc.
|
34
|
+
let (:uid) { generate_uid }
|
35
|
+
|
36
|
+
|
37
|
+
describe "#process!" do
|
38
|
+
let (:file) { file = Paperdragon::File.new(uid) }
|
39
|
+
|
40
|
+
it do
|
41
|
+
metadata = file.process!(logo)
|
42
|
+
|
43
|
+
metadata.must_equal({:width=>216, :height=>63, :uid=>uid})
|
44
|
+
exists?(uid).must_equal true
|
45
|
+
end
|
46
|
+
|
47
|
+
# block
|
48
|
+
it do
|
49
|
+
# puts file.data.size # 9632 bytes
|
50
|
+
file.process!(logo) do |job|
|
51
|
+
job.thumb!("16x16")
|
52
|
+
end
|
53
|
+
|
54
|
+
assert file.data.size <= 457 # smaller after thumb!
|
55
|
+
end
|
56
|
+
|
57
|
+
# additional metadata
|
58
|
+
it do
|
59
|
+
file.process!(logo, :cropping => "16x16") do |job|
|
60
|
+
job.thumb!("16x16")
|
61
|
+
end.must_equal({:width=>16, :height=>5, :uid=>uid, :cropping=>"16x16"})
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
describe "#delete!" do
|
67
|
+
it do
|
68
|
+
file = Paperdragon::File.new(uid)
|
69
|
+
file.process!(logo)
|
70
|
+
exists?(uid).must_equal true
|
71
|
+
|
72
|
+
job = Paperdragon::File.new(uid).delete!
|
73
|
+
|
74
|
+
job.must_equal nil
|
75
|
+
exists?(uid).must_equal false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
describe "#reprocess!" do
|
81
|
+
# existing:
|
82
|
+
let (:file) { Paperdragon::File.new(uid) }
|
83
|
+
let (:original) { Paperdragon::File.new("original/#{uid}") }
|
84
|
+
let (:new_uid) { generate_uid }
|
85
|
+
|
86
|
+
before do
|
87
|
+
original.process!(logo) # original/uid exists.
|
88
|
+
exists?(original.uid).must_equal true
|
89
|
+
file.process!(logo)
|
90
|
+
exists?(file.uid).must_equal true # file to be reprocessed exists (to test delete).
|
91
|
+
end
|
92
|
+
|
93
|
+
it do
|
94
|
+
metadata = file.reprocess!(new_uid, original)
|
95
|
+
|
96
|
+
# it
|
97
|
+
metadata.must_equal({:width=>216, :height=>63, :uid=>new_uid})
|
98
|
+
# it
|
99
|
+
exists?(uid).must_equal false # deleted
|
100
|
+
exists?(new_uid).must_equal true
|
101
|
+
end
|
102
|
+
|
103
|
+
it do
|
104
|
+
job = file.reprocess!(new_uid, original) do |j|
|
105
|
+
j.thumb!("16x16")
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
assert file.data.size <= 457
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
describe "#rename!" do
|
115
|
+
# existing:
|
116
|
+
let (:file) { Paperdragon::File.new(uid, :size => 99) }
|
117
|
+
let (:original) { Paperdragon::File.new(uid) }
|
118
|
+
let (:new_uid) { generate_uid }
|
119
|
+
|
120
|
+
before do
|
121
|
+
original.process!(logo)
|
122
|
+
exists?(uid).must_equal true
|
123
|
+
end
|
124
|
+
|
125
|
+
it do
|
126
|
+
metadata = file.rename!(new_uid) do |uid, new_uid|
|
127
|
+
File.rename("public/paperdragon/"+uid, "public/paperdragon/"+new_uid) # DISCUSS: should that be simpler?
|
128
|
+
end
|
129
|
+
|
130
|
+
# it
|
131
|
+
# metadata.must_equal({:width=>216, :height=>63, :uid=>new_uid, :content_type=>"application/octet-stream", :size=>9632})
|
132
|
+
metadata.must_equal(:uid=>new_uid, :size => 99) # we DON'T fetch original metadata here anymore.
|
133
|
+
|
134
|
+
exists?(uid).must_equal false # deleted
|
135
|
+
exists?(new_uid).must_equal true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
Binary file
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MetadataTest < MiniTest::Spec
|
4
|
+
describe "valid" do
|
5
|
+
let (:valid) {
|
6
|
+
{
|
7
|
+
:original=>{:width=>960, :height=>960, :uid=>"403661339/kristylee-38.jpg", :content_type=>"image/jpeg", :size=>198299},
|
8
|
+
:thumb =>{:width=>191, :height=>191, :uid=>"ds3661339/kristylee-38.jpg", :content_type=>"image/jpeg", :size=>18132}
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
subject { Paperdragon::Metadata.new(valid) }
|
13
|
+
|
14
|
+
it { subject.populated?.must_equal true }
|
15
|
+
it { subject[:original][:width].must_equal 960 }
|
16
|
+
it { subject[:original][:uid].must_equal "403661339/kristylee-38.jpg" }
|
17
|
+
it { subject[:thumb][:uid].must_equal "ds3661339/kristylee-38.jpg" }
|
18
|
+
|
19
|
+
it { subject[:page].must_equal({}) }
|
20
|
+
it { subject[:page][:width].must_equal nil }
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
describe "nil" do
|
25
|
+
subject { Paperdragon::Metadata.new(nil) }
|
26
|
+
|
27
|
+
it { subject.populated?.must_equal false }
|
28
|
+
it { subject[:page].must_equal({}) }
|
29
|
+
it { subject[:page][:width].must_equal nil }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "empty hash" do
|
33
|
+
subject { Paperdragon::Metadata.new({}) }
|
34
|
+
|
35
|
+
it { subject.populated?.must_equal false }
|
36
|
+
it { subject[:page].must_equal({}) }
|
37
|
+
it { subject[:page][:width].must_equal nil }
|
38
|
+
end
|
39
|
+
|
40
|
+
let (:original) { {:original => {}} }
|
41
|
+
|
42
|
+
# #dup
|
43
|
+
# don't change original hash.
|
44
|
+
it do
|
45
|
+
Paperdragon::Metadata.new(original).dup.merge!(:additional => {})
|
46
|
+
original[:additional].must_equal nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# #merge!
|
50
|
+
it { Paperdragon::Metadata.new(original).merge!(:additional => {}).to_hash.must_equal({:original=>{}, :additional=>{}}) }
|
51
|
+
|
52
|
+
# #to_hash
|
53
|
+
it { Paperdragon::Metadata.new(original).to_hash.must_equal({:original=>{}}) }
|
54
|
+
end
|
data/test/model_test.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PaperdragonModelTest < MiniTest::Spec
|
4
|
+
class Avatar
|
5
|
+
class Photo < Paperdragon::File
|
6
|
+
end
|
7
|
+
|
8
|
+
class Attachment < Paperdragon::Attachment
|
9
|
+
self.file_class = Photo
|
10
|
+
end
|
11
|
+
|
12
|
+
include Paperdragon::Model
|
13
|
+
processable :image, Attachment
|
14
|
+
|
15
|
+
|
16
|
+
def image_meta_data
|
17
|
+
{:thumb => {:uid => "Avatar-thumb"}}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it { Avatar.new.image[:thumb].url.must_equal "/paperdragon/Avatar-thumb" }
|
22
|
+
|
23
|
+
|
24
|
+
# minimum setup
|
25
|
+
class Image
|
26
|
+
include Paperdragon::Model
|
27
|
+
processable :image
|
28
|
+
|
29
|
+
def image_meta_data
|
30
|
+
{:thumb => {:uid => "Avatar-thumb"}}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it { Image.new.image[:thumb].url.must_equal "/paperdragon/Avatar-thumb" }
|
35
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'paperdragon/paperclip'
|
4
|
+
|
5
|
+
class PaperclipUidTest < MiniTest::Spec
|
6
|
+
Uid = Paperdragon::Paperclip::Uid
|
7
|
+
|
8
|
+
let (:options) { {:class_name => :avatars, :attachment => :image, :id => 1234,
|
9
|
+
:style => :original, :updated_at => Time.parse("20-06-2014 9:40:59").to_i,
|
10
|
+
:file_name => "kristylee.jpg", :hash_secret => "secret"} }
|
11
|
+
|
12
|
+
it { Uid.from(options).
|
13
|
+
must_equal "system/avatars/image/000/001/234/9bf15e5874b3234c133f7500e6d615747f709e64/original/kristylee.jpg" }
|
14
|
+
|
15
|
+
|
16
|
+
class UidWithFingerprint < Paperdragon::Paperclip::Uid
|
17
|
+
def call
|
18
|
+
"#{root}/#{class_name}/#{attachment}/#{id_partition}/#{hash}/#{style}/#{fingerprint}-#{file_name}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it { UidWithFingerprint.from(options.merge(:fingerprint => 8675309)).
|
23
|
+
must_equal "system/avatars/image/000/001/234/9bf15e5874b3234c133f7500e6d615747f709e64/original/8675309-kristylee.jpg" }
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
class PaperclipModelTest < MiniTest::Spec
|
28
|
+
class Avatar
|
29
|
+
class Photo < Paperdragon::File
|
30
|
+
end
|
31
|
+
|
32
|
+
class Attachment < Paperdragon::Attachment
|
33
|
+
self.file_class = Photo
|
34
|
+
|
35
|
+
def exists?
|
36
|
+
"Of course!"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
include Paperdragon::Paperclip::Model
|
41
|
+
processable :image, Attachment
|
42
|
+
|
43
|
+
|
44
|
+
def image_meta_data
|
45
|
+
{:thumb => {:uid => "Avatar-thumb"}}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# old paperclip style
|
50
|
+
it { Avatar.new.image.url(:thumb).must_equal "/paperdragon/Avatar-thumb" }
|
51
|
+
|
52
|
+
# paperdragon style
|
53
|
+
it { Avatar.new.image[:thumb].url.must_equal "/paperdragon/Avatar-thumb" }
|
54
|
+
|
55
|
+
# delegates all unknown methods back to Attachment.
|
56
|
+
it { Avatar.new.image.exists?.must_equal "Of course!" }
|
57
|
+
end
|
data/test/task_test.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TaskSpec < MiniTest::Spec
|
4
|
+
class Attachment < Paperdragon::Attachment
|
5
|
+
class File < Paperdragon::File
|
6
|
+
end
|
7
|
+
self.file_class= File
|
8
|
+
end
|
9
|
+
|
10
|
+
let (:logo) { Pathname("test/fixtures/apotomo.png") }
|
11
|
+
|
12
|
+
|
13
|
+
# #task allows block and returns metadata hash.
|
14
|
+
describe "#task" do
|
15
|
+
it do
|
16
|
+
Attachment.new(nil).task(logo) do |t|
|
17
|
+
t.process!(:original)
|
18
|
+
t.process!(:thumb) { |j| j.thumb!("16x16") }
|
19
|
+
end.must_equal({:original=>{:width=>216, :height=>63, :uid=>"original-apotomo.png"}, :thumb=>{:width=>16, :height=>5, :uid=>"thumb-apotomo.png"}})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# task without block
|
24
|
+
let (:subject) { Attachment.new(nil).task(logo) }
|
25
|
+
|
26
|
+
describe "#process!" do
|
27
|
+
it do
|
28
|
+
subject.process!(:original)
|
29
|
+
subject.process!(:thumb) { |j| j.thumb!("16x16") }
|
30
|
+
|
31
|
+
subject.metadata_hash.must_equal({:original=>{:width=>216, :height=>63, :uid=>"original-apotomo.png"}, :thumb=>{:width=>16, :height=>5, :uid=>"thumb-apotomo.png"}})
|
32
|
+
end
|
33
|
+
|
34
|
+
it do
|
35
|
+
assert_raises Paperdragon::MissingUploadError do
|
36
|
+
Attachment.new(nil).task.process!(:original)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe "#reprocess!" do
|
43
|
+
let (:original) { Paperdragon::File.new("original/pic.jpg") }
|
44
|
+
|
45
|
+
before do
|
46
|
+
original.process!(logo) # original/uid exists.
|
47
|
+
exists?(original.uid).must_equal true
|
48
|
+
end
|
49
|
+
|
50
|
+
subject { Attachment.new({
|
51
|
+
:original=>{:uid=>"original/pic.jpg"}, :thumb=>{:uid=>"original/thumb.jpg"}}).task
|
52
|
+
}
|
53
|
+
|
54
|
+
# FIXME: fingerprint should be added before .png suffix, idiot!
|
55
|
+
it do
|
56
|
+
subject.reprocess!(:original, "-1", original)
|
57
|
+
subject.reprocess!(:thumb, "-1", original) { |j| j.thumb!("16x16") }
|
58
|
+
|
59
|
+
# it
|
60
|
+
subject.metadata_hash.must_equal({:original=>{:width=>216, :height=>63, :uid=>"original/pic-1.jpg"}, :thumb=>{:width=>16, :height=>5, :uid=>"original/thumb-1.jpg"}})
|
61
|
+
# it
|
62
|
+
# exists?(original.uri).must_equal false # deleted
|
63
|
+
# exists?(new_uid).must_equal true
|
64
|
+
end
|
65
|
+
|
66
|
+
# don't pass in fingerprint+original.
|
67
|
+
it do
|
68
|
+
subject.reprocess!(:thumb) { |j| j.thumb!("24x24") }
|
69
|
+
subject.metadata_hash.must_equal({:original=>{:uid=>"original/pic.jpg"}, :thumb=>{:width=>24, :height=>7, :uid=>"original/thumb.jpg"}})
|
70
|
+
end
|
71
|
+
|
72
|
+
# only process one, should return entire metadata hash
|
73
|
+
it do
|
74
|
+
subject.reprocess!(:thumb, "-new") { |j| j.thumb!("24x24") }
|
75
|
+
subject.metadata_hash.must_equal({:original=>{:uid=>"original/pic.jpg"}, :thumb=>{:width=>24, :height=>7, :uid=>"original/thumb-new.jpg"}})
|
76
|
+
|
77
|
+
# original must be unchanged
|
78
|
+
exists?(Attachment.new(subject.metadata_hash)[:original].uid).must_equal true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
describe "#rename!" do
|
84
|
+
before do
|
85
|
+
attachment = Paperdragon::Attachment.new(nil)
|
86
|
+
@upload_task = attachment.task(logo)
|
87
|
+
metadata = @upload_task.process!(:original).must_equal({:original=>{:width=>216, :height=>63, :uid=>"original-apotomo.png"}})
|
88
|
+
|
89
|
+
# we do not update the attachment from task.
|
90
|
+
attachment = Paperdragon::Attachment.new(@upload_task.metadata)
|
91
|
+
exists?(attachment[:original].uid).must_equal true
|
92
|
+
end
|
93
|
+
|
94
|
+
let (:metadata) { @upload_task.metadata }
|
95
|
+
|
96
|
+
# let (:subject) { Attachment.new(nil).task }
|
97
|
+
it do
|
98
|
+
attachment = Paperdragon::Attachment.new(metadata) # {:original=>{:width=>216, :height=>63, :uid=>"uid/original", :size=>9632}}
|
99
|
+
task = attachment.task
|
100
|
+
task.rename!(:original, "-new") { |uid, new_uid|
|
101
|
+
File.rename("public/paperdragon/"+uid, "public/paperdragon/"+new_uid)
|
102
|
+
}.must_equal({:original=>{:width=>216, :height=>63, :uid=>"original-apotomo-new.png"}})
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paperdragon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dragonfly
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: uber
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,7 +66,21 @@ dependencies:
|
|
38
66
|
- - ">="
|
39
67
|
- !ruby/object:Gem::Version
|
40
68
|
version: '0'
|
41
|
-
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Explicit image processing based on Dragonfly with Paperclip compatibility.
|
42
84
|
email:
|
43
85
|
- apotonick@gmail.com
|
44
86
|
executables: []
|
@@ -51,8 +93,24 @@ files:
|
|
51
93
|
- README.md
|
52
94
|
- Rakefile
|
53
95
|
- lib/paperdragon.rb
|
96
|
+
- lib/paperdragon/attachment.rb
|
97
|
+
- lib/paperdragon/file.rb
|
98
|
+
- lib/paperdragon/file/operations.rb
|
99
|
+
- lib/paperdragon/metadata.rb
|
100
|
+
- lib/paperdragon/model.rb
|
101
|
+
- lib/paperdragon/paperclip.rb
|
102
|
+
- lib/paperdragon/paperclip/model.rb
|
103
|
+
- lib/paperdragon/task.rb
|
54
104
|
- lib/paperdragon/version.rb
|
55
105
|
- paperdragon.gemspec
|
106
|
+
- test/attachment_test.rb
|
107
|
+
- test/file_test.rb
|
108
|
+
- test/fixtures/apotomo.png
|
109
|
+
- test/metadata_test.rb
|
110
|
+
- test/model_test.rb
|
111
|
+
- test/paperclip_uid_test.rb
|
112
|
+
- test/task_test.rb
|
113
|
+
- test/test_helper.rb
|
56
114
|
homepage: ''
|
57
115
|
licenses:
|
58
116
|
- MIT
|
@@ -76,6 +134,14 @@ rubyforge_project:
|
|
76
134
|
rubygems_version: 2.2.1
|
77
135
|
signing_key:
|
78
136
|
specification_version: 4
|
79
|
-
summary:
|
80
|
-
test_files:
|
137
|
+
summary: Explicit image processing based on Dragonfly with Paperclip compatibility.
|
138
|
+
test_files:
|
139
|
+
- test/attachment_test.rb
|
140
|
+
- test/file_test.rb
|
141
|
+
- test/fixtures/apotomo.png
|
142
|
+
- test/metadata_test.rb
|
143
|
+
- test/model_test.rb
|
144
|
+
- test/paperclip_uid_test.rb
|
145
|
+
- test/task_test.rb
|
146
|
+
- test/test_helper.rb
|
81
147
|
has_rdoc:
|