paperdragon 0.0.1 → 0.0.2
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.
- 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:
|