attachs 0.4.5 → 4.0.0.0
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/MIT-LICENSE +1 -1
- data/README.md +83 -218
- data/Rakefile +1 -14
- data/lib/attachs.rb +98 -31
- data/lib/attachs/attachment.rb +301 -72
- data/lib/attachs/builder.rb +78 -0
- data/lib/attachs/collection.rb +84 -0
- data/lib/attachs/concern.rb +47 -0
- data/lib/attachs/configuration.rb +11 -0
- data/lib/attachs/console.rb +40 -0
- data/lib/attachs/extensions/active_record/base.rb +24 -0
- data/lib/attachs/extensions/active_record/validations/attachment_content_type_validator.rb +37 -0
- data/lib/attachs/extensions/active_record/validations/attachment_presence_validator.rb +27 -0
- data/lib/attachs/extensions/active_record/validations/attachment_size_validator.rb +55 -0
- data/lib/attachs/extensions/active_record/validations/attachment_validator.rb +24 -0
- data/lib/attachs/interpolations.rb +27 -0
- data/lib/attachs/jobs/base.rb +14 -0
- data/lib/attachs/jobs/delete_job.rb +15 -0
- data/lib/attachs/jobs/update_job.rb +11 -0
- data/lib/attachs/locales/en.yml +2 -6
- data/lib/attachs/locales/es.yml +2 -6
- data/lib/attachs/processors/base.rb +4 -14
- data/lib/attachs/processors/image.rb +45 -0
- data/lib/attachs/railtie.rb +7 -27
- data/lib/attachs/storages/base.rb +10 -59
- data/lib/attachs/storages/s3.rb +68 -63
- data/lib/attachs/version.rb +1 -1
- data/lib/generators/attachs/install/install_generator.rb +15 -0
- data/lib/generators/attachs/install/templates/initializer.rb +7 -0
- data/lib/generators/attachs/upload/templates/migration.rb +11 -0
- data/lib/generators/attachs/upload/templates/model.rb +19 -0
- data/lib/generators/attachs/upload/upload_generator.rb +24 -0
- data/lib/tasks/attachs.rake +12 -26
- data/test/attachment_test.rb +175 -12
- data/test/collection_test.rb +52 -0
- data/test/concern_test.rb +10 -0
- data/test/dummy/Rakefile +1 -2
- data/test/dummy/app/assets/javascripts/application.js +2 -2
- data/test/dummy/app/assets/stylesheets/application.css +6 -4
- data/test/dummy/app/models/product.rb +16 -0
- data/test/dummy/app/models/shop.rb +14 -0
- data/test/dummy/app/models/upload.rb +19 -0
- data/test/dummy/app/views/layouts/application.html.erb +9 -11
- data/test/dummy/bin/bundle +1 -0
- data/test/dummy/bin/rails +2 -1
- data/test/dummy/bin/rake +1 -0
- data/test/dummy/bin/setup +30 -0
- data/test/dummy/config.ru +1 -1
- data/test/dummy/config/application.rb +3 -2
- data/test/dummy/config/boot.rb +1 -1
- data/test/dummy/config/database.yml +4 -22
- data/test/dummy/config/database.yml.travis +3 -0
- data/test/dummy/config/environment.rb +1 -1
- data/test/dummy/config/environments/development.rb +15 -3
- data/test/dummy/config/environments/production.rb +21 -26
- data/test/dummy/config/environments/test.rb +10 -12
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/attachs.rb +7 -21
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/mime_types.rb +1 -2
- data/test/dummy/config/initializers/session_store.rb +1 -1
- data/test/dummy/config/routes.rb +54 -0
- data/test/dummy/config/secrets.yml +24 -0
- data/test/dummy/db/migrate/20161024234003_create_shops.rb +11 -0
- data/test/dummy/db/migrate/20161024234029_create_products.rb +12 -0
- data/test/dummy/db/migrate/20161031135453_create_uploads.rb +11 -0
- data/test/dummy/db/schema.rb +27 -16
- data/test/dummy/log/development.log +61 -0
- data/test/dummy/log/test.log +5873 -0
- data/test/dummy/public/404.html +58 -55
- data/test/dummy/public/422.html +58 -55
- data/test/dummy/public/500.html +57 -54
- data/test/fixtures/big_file.txt +50 -0
- data/test/fixtures/big_image.jpg +0 -0
- data/test/fixtures/file.txt +1 -0
- data/test/fixtures/image.jpg +0 -0
- data/test/{dummy/test/fixtures/file.txt → fixtures/small_file.txt} +0 -0
- data/test/fixtures/small_image.jpg +0 -0
- data/test/generator_test.rb +14 -6
- data/test/helper_test.rb +28 -0
- data/test/query_test.rb +26 -0
- data/test/support/storage_helper.rb +65 -0
- data/test/task_test.rb +69 -0
- data/test/test_helper.rb +6 -45
- data/test/upload_test.rb +27 -0
- data/test/validator_test.rb +160 -0
- metadata +114 -68
- data/lib/attachs/active_record/base.rb +0 -103
- data/lib/attachs/active_record/connection_adapters.rb +0 -40
- data/lib/attachs/active_record/migration.rb +0 -17
- data/lib/attachs/active_record/validators.rb +0 -7
- data/lib/attachs/active_record/validators/attachment_content_type_validator.rb +0 -30
- data/lib/attachs/active_record/validators/attachment_presence_validator.rb +0 -22
- data/lib/attachs/active_record/validators/attachment_size_validator.rb +0 -47
- data/lib/attachs/processors/thumbnail.rb +0 -69
- data/lib/attachs/storages/local.rb +0 -95
- data/lib/attachs/types/base.rb +0 -22
- data/lib/attachs/types/default.rb +0 -29
- data/lib/attachs/types/regular.rb +0 -21
- data/lib/generators/attachs/install_generator.rb +0 -13
- data/lib/generators/attachs/templates/attachs.rb +0 -2
- data/test/dummy/README.rdoc +0 -28
- data/test/dummy/app/models/medium.rb +0 -5
- data/test/dummy/app/models/picture.rb +0 -2
- data/test/dummy/config/initializers/secret_token.rb +0 -13
- data/test/dummy/config/s3.yml +0 -15
- data/test/dummy/db/migrate/20140808012639_create_media.rb +0 -11
- data/test/dummy/test/fixtures/image.gif +0 -0
- data/test/local_storage_test.rb +0 -72
- data/test/migration_test.rb +0 -65
- data/test/processor_test.rb +0 -49
- data/test/s3_storage_tes.rb +0 -66
- data/test/tasks_test.rb +0 -57
- data/test/validators_test.rb +0 -100
@@ -1,103 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module ActiveRecord
|
3
|
-
module Base
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
protected
|
7
|
-
|
8
|
-
def attachments
|
9
|
-
@attachments ||= {}.tap do |instances|
|
10
|
-
self.class.attachments.each do |attr, options|
|
11
|
-
instances[attr] = Attachs::Attachment.new(self, attr, options)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def queued_attachments
|
17
|
-
@queued_attachments ||= { process: {}, destroy: {} }
|
18
|
-
end
|
19
|
-
|
20
|
-
def queue_attachment(attr, options, value)
|
21
|
-
unless queued_attachments[:destroy].has_key? attr
|
22
|
-
queued_attachments[:destroy][attr] = attachments[attr]
|
23
|
-
end
|
24
|
-
if queued_attachments[:process].has_key? attr and value.nil?
|
25
|
-
attachments[attr]
|
26
|
-
else
|
27
|
-
queued_attachments[:process][attr] = Attachs::Attachment.new(self, attr, options, value)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def commit_attachments
|
32
|
-
self.class.attachments.each do |attr, options|
|
33
|
-
send "destroy_#{attr}=", nil
|
34
|
-
end
|
35
|
-
queued_attachments[:destroy].each do |attr, attachment|
|
36
|
-
attachment.destroy
|
37
|
-
end
|
38
|
-
queued_attachments[:process].each do |attr, attachment|
|
39
|
-
attachment.process
|
40
|
-
end
|
41
|
-
queued_attachments[:destroy] = {}
|
42
|
-
queued_attachments[:process] = {}
|
43
|
-
end
|
44
|
-
|
45
|
-
def destroy_attachments
|
46
|
-
self.class.attachments.each do |attr, options|
|
47
|
-
send(attr).destroy
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
module ClassMethods
|
52
|
-
|
53
|
-
def has_attached_file(*args)
|
54
|
-
options = args.extract_options!
|
55
|
-
unless attachable?
|
56
|
-
make_attachable
|
57
|
-
end
|
58
|
-
args.each do |attr|
|
59
|
-
define_attachment_methods attr, options
|
60
|
-
attachments[attr] = options
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def attachments
|
65
|
-
@attachments ||= {}
|
66
|
-
end
|
67
|
-
|
68
|
-
def attachable?
|
69
|
-
attachments.any?
|
70
|
-
end
|
71
|
-
|
72
|
-
def inherited(subclass)
|
73
|
-
subclass.instance_variable_set :@attachments, @attachments
|
74
|
-
super
|
75
|
-
end
|
76
|
-
|
77
|
-
protected
|
78
|
-
|
79
|
-
def make_attachable
|
80
|
-
after_save :commit_attachments
|
81
|
-
after_destroy :destroy_attachments
|
82
|
-
end
|
83
|
-
|
84
|
-
def define_attachment_methods(attr, options)
|
85
|
-
define_method "#{attr}=" do |value|
|
86
|
-
attachments[attr] = queue_attachment(attr, options, value)
|
87
|
-
end
|
88
|
-
define_method attr do
|
89
|
-
attachments[attr]
|
90
|
-
end
|
91
|
-
attr_reader "destroy_#{attr}"
|
92
|
-
define_method "destroy_#{attr}=" do |value|
|
93
|
-
instance_variable_set "@destroy_#{attr}", value
|
94
|
-
if [1, '1', true].include?(value) and !send(attr).upload?
|
95
|
-
send "#{attr}=", nil
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module ActiveRecord
|
3
|
-
module ConnectionAdapters
|
4
|
-
COLUMNS = {
|
5
|
-
filename: 'string',
|
6
|
-
content_type: 'string',
|
7
|
-
size: 'integer',
|
8
|
-
updated_at: 'datetime'
|
9
|
-
}
|
10
|
-
|
11
|
-
module AbstractAdapter
|
12
|
-
def add_attachment(table_name, *attachment_names)
|
13
|
-
attachment_names.each do |attachment_name|
|
14
|
-
COLUMNS.each do |column_name, column_type|
|
15
|
-
add_column table_name, "#{attachment_name}_#{column_name}", column_type
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def remove_attachment(table_name, *attachment_names)
|
21
|
-
attachment_names.each do |attachment_name|
|
22
|
-
COLUMNS.each do |column_name, column_type|
|
23
|
-
remove_column table_name, "#{attachment_name}_#{column_name}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
module TableDefinition
|
30
|
-
def attachment(*attachment_names)
|
31
|
-
attachment_names.each do |attachment_name|
|
32
|
-
COLUMNS.each do |column_name, column_type|
|
33
|
-
column "#{attachment_name}_#{column_name}", column_type
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module ActiveRecord
|
3
|
-
module Migration
|
4
|
-
module CommandRecorder
|
5
|
-
def add_attachment(*args)
|
6
|
-
record :add_attachment, args
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
def invert_add_attachment(*args)
|
12
|
-
[:remove_attachment, args]
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module ActiveRecord
|
3
|
-
module Validators
|
4
|
-
class AttachmentContentTypeValidator < ActiveModel::EachValidator
|
5
|
-
|
6
|
-
def validate_each(record, attribute, value)
|
7
|
-
if value.exists?
|
8
|
-
if options.has_key? :with
|
9
|
-
if options[:with] !~ value.content_type
|
10
|
-
record.errors.add attribute, :attachment_content_type_with
|
11
|
-
end
|
12
|
-
elsif options.has_key? :in
|
13
|
-
if options[:in].exclude? value.content_type
|
14
|
-
record.errors.add attribute, :attachment_content_type_in, types: options[:in].to_sentence
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
module ClassMethods
|
22
|
-
|
23
|
-
def validates_attachment_content_type_of(*attr_names)
|
24
|
-
validates_with AttachmentContentTypeValidator, _merge_attributes(attr_names)
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module ActiveRecord
|
3
|
-
module Validators
|
4
|
-
class AttachmentPresenceValidator < ActiveModel::EachValidator
|
5
|
-
|
6
|
-
def validate_each(record, attribute, value)
|
7
|
-
if value.blank?
|
8
|
-
record.errors.add attribute, :attachment_presence
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
end
|
13
|
-
module ClassMethods
|
14
|
-
|
15
|
-
def validates_attachment_presence_of(*attr_names)
|
16
|
-
validates_with AttachmentPresenceValidator, _merge_attributes(attr_names)
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module ActiveRecord
|
3
|
-
module Validators
|
4
|
-
class AttachmentSizeValidator < ActiveModel::EachValidator
|
5
|
-
include ActionView::Helpers::NumberHelper
|
6
|
-
|
7
|
-
def validate_each(record, attribute, value)
|
8
|
-
if value.exists?
|
9
|
-
if options.has_key? :in
|
10
|
-
if options[:in].exclude? value.size
|
11
|
-
record.errors.add(
|
12
|
-
attribute,
|
13
|
-
:attachment_size_in,
|
14
|
-
min: number_to_human_size(options[:in].begin),
|
15
|
-
max: number_to_human_size(options[:in].end)
|
16
|
-
)
|
17
|
-
end
|
18
|
-
else
|
19
|
-
if options.has_key? :less_than and value.size > options[:less_than]
|
20
|
-
record.errors.add(
|
21
|
-
attribute,
|
22
|
-
:attachment_size_less_than,
|
23
|
-
count: number_to_human_size(options[:less_than])
|
24
|
-
)
|
25
|
-
end
|
26
|
-
if options.has_key? :greater_than and value.size < options[:greater_than]
|
27
|
-
record.errors.add(
|
28
|
-
attribute,
|
29
|
-
:attachment_size_greater_than,
|
30
|
-
count: number_to_human_size(options[:greater_than])
|
31
|
-
)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
module ClassMethods
|
39
|
-
|
40
|
-
def validates_attachment_size_of(*attr_names)
|
41
|
-
validates_with AttachmentSizeValidator, _merge_attributes(attr_names)
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module Processors
|
3
|
-
class Thumbnail < Base
|
4
|
-
|
5
|
-
def initialize(attachment, source)
|
6
|
-
super
|
7
|
-
@width, @height = dimensions(source)
|
8
|
-
end
|
9
|
-
|
10
|
-
def process(style, destination)
|
11
|
-
new_width, new_height, strategy, options = geometry(style)
|
12
|
-
resize source, width, height, new_width, new_height, strategy, options, destination
|
13
|
-
end
|
14
|
-
|
15
|
-
protected
|
16
|
-
|
17
|
-
attr_reader :width, :height
|
18
|
-
|
19
|
-
def geometry(style)
|
20
|
-
geometry = Attachs.config.styles[style]
|
21
|
-
width, height = geometry.scan(/[^x]+/).map(&:to_i)
|
22
|
-
case geometry[/!|#/]
|
23
|
-
when '#'
|
24
|
-
strategy = 'cover'
|
25
|
-
when '!'
|
26
|
-
strategy = 'force'
|
27
|
-
else
|
28
|
-
strategy = 'contain'
|
29
|
-
end
|
30
|
-
options = Attachs.config.convert_options[style]
|
31
|
-
[width, height, strategy, options]
|
32
|
-
end
|
33
|
-
|
34
|
-
def resize(source, width, height, new_width, new_height, strategy, custom_options, destination)
|
35
|
-
case strategy
|
36
|
-
when 'cover'
|
37
|
-
ratio = [new_width.to_f/width, new_height.to_f/height].max
|
38
|
-
options = "-resize #{(ratio*width).ceil}x#{(ratio*height).ceil} -gravity center -crop #{new_width}x#{new_height}+0+0"
|
39
|
-
when 'force'
|
40
|
-
options = "-resize #{new_width}x#{new_height}\!"
|
41
|
-
when 'contain'
|
42
|
-
options = "-resize #{new_width}x#{new_height}"
|
43
|
-
end
|
44
|
-
if global_options = Attachs.config.global_convert_options
|
45
|
-
options << " #{global_options}"
|
46
|
-
end
|
47
|
-
if custom_options
|
48
|
-
options << " #{custom_options}"
|
49
|
-
end
|
50
|
-
convert source, options, destination
|
51
|
-
end
|
52
|
-
|
53
|
-
def dimensions(source)
|
54
|
-
if output = identify(source, '-format %wx%h')
|
55
|
-
output.split('x').map(&:to_i)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def convert(source, options, destination)
|
60
|
-
run "convert '#{source}' #{options} '#{destination}'"
|
61
|
-
end
|
62
|
-
|
63
|
-
def identify(source, options)
|
64
|
-
run "identify #{options} '#{source}'"
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module Storages
|
3
|
-
class Local < Base
|
4
|
-
|
5
|
-
def url(*args)
|
6
|
-
if attachment.url?
|
7
|
-
options = args.extract_options!
|
8
|
-
style = (args[0] || :original)
|
9
|
-
base_url.join(path(style)).to_s.tap do |url|
|
10
|
-
if find_option(options, :cachebuster, Attachs.config.cachebuster)
|
11
|
-
url << "?#{attachment.updated_at.to_i}"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def process(force=false)
|
18
|
-
if attachment.upload?
|
19
|
-
FileUtils.mkdir_p realpath.dirname
|
20
|
-
attachment.upload.rewind
|
21
|
-
File.open(realpath, 'wb') do |file|
|
22
|
-
while chunk = attachment.upload.read(16 * 1024)
|
23
|
-
file.write chunk
|
24
|
-
end
|
25
|
-
end
|
26
|
-
attachment.uploaded!
|
27
|
-
end
|
28
|
-
process_styles force
|
29
|
-
end
|
30
|
-
|
31
|
-
def process_styles(force=false)
|
32
|
-
if attachment.image?
|
33
|
-
attachment.processors.each do |klass|
|
34
|
-
processor = klass.new(attachment, realpath)
|
35
|
-
attachment.styles.each do |style|
|
36
|
-
if force == true
|
37
|
-
delete realpath(style)
|
38
|
-
end
|
39
|
-
unless File.exist? realpath(style)
|
40
|
-
FileUtils.mkdir_p realpath(style).dirname
|
41
|
-
processor.process style, realpath(style)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def destroy
|
49
|
-
delete realpath
|
50
|
-
destroy_styles
|
51
|
-
end
|
52
|
-
|
53
|
-
def destroy_styles
|
54
|
-
if attachment.image?
|
55
|
-
attachment.styles.each do |style|
|
56
|
-
delete realpath(style)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
protected
|
62
|
-
|
63
|
-
def delete(realpath)
|
64
|
-
if File.exist? realpath
|
65
|
-
File.delete realpath
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def realpath(style=:original)
|
70
|
-
base_path.join(path(style))
|
71
|
-
end
|
72
|
-
|
73
|
-
def base_path
|
74
|
-
@base_path ||= begin
|
75
|
-
if attachment.private?
|
76
|
-
Rails.root.join 'private'
|
77
|
-
else
|
78
|
-
Rails.root.join 'public'
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def base_url
|
84
|
-
@base_url ||= begin
|
85
|
-
if Attachs.config.base_url.present?
|
86
|
-
Pathname.new Attachs.config.base_url
|
87
|
-
else
|
88
|
-
Pathname.new '/'
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
data/lib/attachs/types/base.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module Attachs
|
2
|
-
module Types
|
3
|
-
class Base
|
4
|
-
|
5
|
-
def initialize(attachment)
|
6
|
-
@attachment = attachment
|
7
|
-
end
|
8
|
-
|
9
|
-
protected
|
10
|
-
|
11
|
-
attr_reader :attachment
|
12
|
-
|
13
|
-
def storage
|
14
|
-
@storage ||= begin
|
15
|
-
klass = (attachment.options[:storage] || Attachs.config.default_storage).to_s.classify
|
16
|
-
Attachs::Storages.const_get(klass).new(attachment)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|