attachs 0.3.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +98 -0
- data/Rakefile +34 -0
- data/app/controllers/attachs/presets_controller.rb +17 -0
- data/config/locales/en.yml +8 -0
- data/config/locales/es.yml +8 -0
- data/config/routes.rb +5 -0
- data/lib/attachs.rb +16 -0
- data/lib/attachs/active_record/base.rb +145 -0
- data/lib/attachs/engine.rb +7 -0
- data/lib/attachs/magick/image.rb +85 -0
- data/lib/attachs/railtie.rb +20 -0
- data/lib/attachs/storages/local.rb +61 -0
- data/lib/attachs/storages/s3.rb +77 -0
- data/lib/attachs/types/file.rb +113 -0
- data/lib/attachs/types/image.rb +40 -0
- data/lib/attachs/validators/attachment_content_type_validator.rb +11 -0
- data/lib/attachs/validators/attachment_presence_validator.rb +9 -0
- data/lib/attachs/validators/attachment_size_validator.rb +27 -0
- data/lib/attachs/validators/base.rb +13 -0
- data/lib/attachs/version.rb +5 -0
- data/lib/generators/attachs/s3/config_generator.rb +13 -0
- data/lib/generators/attachs/s3/templates/s3.yml +16 -0
- data/lib/tasks/attachs_tasks.rake +71 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/all_attached.rb +16 -0
- data/test/dummy/app/models/file_attached.rb +3 -0
- data/test/dummy/app/models/image_attached.rb +3 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +29 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20130820222342_create_file_attacheds.rb +9 -0
- data/test/dummy/db/migrate/20130820222355_create_image_attacheds.rb +9 -0
- data/test/dummy/db/migrate/20130820222534_create_all_attacheds.rb +19 -0
- data/test/dummy/db/schema.rb +44 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +28 -0
- data/test/dummy/log/test.log +8545 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/uploads/files/file.txt +1 -0
- data/test/dummy/public/uploads/images/big/image.jpg +0 -0
- data/test/dummy/public/uploads/images/original/image.jpg +0 -0
- data/test/dummy/public/uploads/images/small/image.jpg +0 -0
- data/test/dummy/test/fixtures/file.txt +1 -0
- data/test/dummy/test/fixtures/file_big.txt +1 -0
- data/test/dummy/test/fixtures/file_empty.txt +1 -0
- data/test/dummy/test/fixtures/image.jpg +0 -0
- data/test/dummy/test/fixtures/image_big.jpg +0 -0
- data/test/dummy/test/fixtures/image_empty.jpg +0 -0
- data/test/local_file_record_test.rb +20 -0
- data/test/local_file_string_test.rb +23 -0
- data/test/local_file_upload_test.rb +30 -0
- data/test/local_generate_test.rb +25 -0
- data/test/local_image_record_test.rb +25 -0
- data/test/local_image_string_test.rb +34 -0
- data/test/local_image_upload_test.rb +27 -0
- data/test/local_records_test.rb +52 -0
- data/test/local_validators_test.rb +71 -0
- data/test/s3_file_record_tes.rb +21 -0
- data/test/s3_file_string_tes.rb +24 -0
- data/test/s3_file_upload_tes.rb +31 -0
- data/test/s3_image_record_tes.rb +26 -0
- data/test/s3_image_string_tes.rb +31 -0
- data/test/s3_image_upload_tes.rb +28 -0
- data/test/s3_records_tes.rb +56 -0
- data/test/s3_validators_tes.rb +72 -0
- data/test/test_helper.rb +49 -0
- metadata +250 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 54ae49b0a6e0cb9b5e170ac1b1559dd6e614055b
|
|
4
|
+
data.tar.gz: 273fcb21962f1b648de5d86064775d9b9b0dfddb
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2512efff14fad943789edf5867e952c558fed32fb25d31f4fde483015784d1cd2d5603075a2e33109b1b0fd8b3fce7ad8a74f0a2291265d60306f1cdadc8f6cd
|
|
7
|
+
data.tar.gz: c089f8e848fd436b8491144bf3a0ef57c00be075145cd8f495e6834faaf9333a1774efb37d621da9639da77dac5265a7572e0a7c417a340a69e5aeb542e9ab6b
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2013 Mattways
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{<img src="https://codeclimate.com/github/mattways/attachs.png" />}[https://codeclimate.com/github/mattways/attachs] {<img src="https://travis-ci.org/mattways/attachs.png?branch=master" alt="Build Status" />}[https://travis-ci.org/mattways/attachs] {<img src="https://gemnasium.com/mattways/attachs.png" alt="Dependency Status" />}[https://gemnasium.com/mattways/attachs]
|
|
2
|
+
|
|
3
|
+
= Attachs
|
|
4
|
+
|
|
5
|
+
Minimalistic toolkit to attach file and images to records.
|
|
6
|
+
|
|
7
|
+
= Install
|
|
8
|
+
|
|
9
|
+
Put this line in your Gemfile:
|
|
10
|
+
gem 'attachs'
|
|
11
|
+
|
|
12
|
+
Then bundle:
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
ImageMagick must be install, you can install it with homebrew:
|
|
16
|
+
brew install imagemagick
|
|
17
|
+
|
|
18
|
+
= Usage
|
|
19
|
+
|
|
20
|
+
Mount the engine at the end of you routes.rb (only if you are using local storage):
|
|
21
|
+
mount Attachs:Engine: '/' # Will be use to generate on the fly missing image presets
|
|
22
|
+
|
|
23
|
+
Add the column to your table (just a string):
|
|
24
|
+
create_table :models do |t|
|
|
25
|
+
t.string :attr
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
If you need a file:
|
|
29
|
+
class Model < ActiveRecord::Base
|
|
30
|
+
has_attached_file :attr, default: 'file.txt'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
If you need a image:
|
|
34
|
+
class Model < ActiveRecord::Base
|
|
35
|
+
has_attached_image :attr, presets: [:small, :big], default: 'assets/image.jpg'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Define presets in your application.rb
|
|
39
|
+
config.attachs.presets = {
|
|
40
|
+
big: { method: :fit, width: 1024, height: 768 }, # Fit will scale the image until it fit in the space without cropping
|
|
41
|
+
small: { method: :fill, width: 120, height: :120 }, # Fill will scale the image to fill all the space and then crop
|
|
42
|
+
custom: proc { |image| image.convert resize: '100x100' } # ImageMagick wrapper to do whatever you want
|
|
43
|
+
}
|
|
44
|
+
config.attachs.default_presets = [:small] # Define the default presets for all models with attached images
|
|
45
|
+
config.attachs.storage = :local # The default it's local, you can use :s3 as well
|
|
46
|
+
|
|
47
|
+
If you want to use S3 create a s3.yml config file with this generator and complete it:
|
|
48
|
+
rails g attachs:s3:config
|
|
49
|
+
|
|
50
|
+
The validation works very similar to paperclip:
|
|
51
|
+
class Model < ActiveRecord::Base
|
|
52
|
+
has_attached_file :attr
|
|
53
|
+
validates :attr, attachment_presence: true, attachment_size: { in: 0..4.megabytes }, attachment_content_type: { in: ['txt'] }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
If you want to translate the errores the keys are:
|
|
57
|
+
errors.messages.attachment_presence
|
|
58
|
+
errors.messages.attachment_size_in # :max and :min
|
|
59
|
+
errors.messages.attachment_size_less_than # :count
|
|
60
|
+
errors.messages.attachment_size_greater_than # :count
|
|
61
|
+
errors.messages.attachment_content_type # :types
|
|
62
|
+
|
|
63
|
+
In your views:
|
|
64
|
+
a{ href: record.file.url } # To get the file url
|
|
65
|
+
a{ href: record.image.url } # To get the original image
|
|
66
|
+
a{ href: record.image.url(:big) } # To get the a thumb
|
|
67
|
+
|
|
68
|
+
In your forms:
|
|
69
|
+
= f.file_field :attr
|
|
70
|
+
|
|
71
|
+
If your file it's optional:
|
|
72
|
+
= f.check_box :delete_attr
|
|
73
|
+
|
|
74
|
+
= FAQ
|
|
75
|
+
|
|
76
|
+
== How can I use a cdn with this plugin?
|
|
77
|
+
|
|
78
|
+
Define a base url in your application.rb:
|
|
79
|
+
config.attachs.base_url = 'http://cdn.example.com'
|
|
80
|
+
|
|
81
|
+
== How can I automatically create buckets?
|
|
82
|
+
|
|
83
|
+
Use this rake task after create the s3.yml file:
|
|
84
|
+
rake attachs:s3:buckets:create
|
|
85
|
+
|
|
86
|
+
== How can I clean a preset?
|
|
87
|
+
|
|
88
|
+
Use this rake task:
|
|
89
|
+
rake attachs:presets:clean MODEL=models PRESETS=first_preset,second_preset
|
|
90
|
+
|
|
91
|
+
== How can I refresh a preset?
|
|
92
|
+
|
|
93
|
+
Use this rake task:
|
|
94
|
+
rake attachs:presets:refresh MODEL=models PRESETS=first_preset,second_preset
|
|
95
|
+
|
|
96
|
+
== How can I migrate from versions before 0.3.0
|
|
97
|
+
|
|
98
|
+
All will work the same except from the name of the config parameter, replace uploads with attachs.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'Attachs'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
|
18
|
+
load 'rails/tasks/engine.rake'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Bundler::GemHelper.install_tasks
|
|
23
|
+
|
|
24
|
+
require 'rake/testtask'
|
|
25
|
+
|
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
|
27
|
+
t.libs << 'lib'
|
|
28
|
+
t.libs << 'test'
|
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
|
30
|
+
t.verbose = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
task default: :test
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Attachs
|
|
2
|
+
class PresetsController < ActionController::Base
|
|
3
|
+
|
|
4
|
+
def generate
|
|
5
|
+
filename = "#{params[:image]}.#{params[:format]}"
|
|
6
|
+
preset = params[:preset].gsub('-', '_').to_sym
|
|
7
|
+
if Rails.application.config.attachs.presets.has_key? preset
|
|
8
|
+
image = Attachs::Types::Image.new(filename)
|
|
9
|
+
if image.exists? and !image.exists?(preset)
|
|
10
|
+
image.generate_preset preset
|
|
11
|
+
redirect_to image.url(params[:preset]), cb: Random.rand(100000)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
en:
|
|
2
|
+
errors:
|
|
3
|
+
messages:
|
|
4
|
+
attachment_presence: "can't be blank"
|
|
5
|
+
attachment_content_type: "the allowed types are: %{types}"
|
|
6
|
+
attachment_size_less_than: "size must be less than %{count}"
|
|
7
|
+
attachment_size_greater_than: "size must be greater than %{count}"
|
|
8
|
+
attachment_size_in: "size must be between %{min} and %{max}"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
es:
|
|
2
|
+
errors:
|
|
3
|
+
messages:
|
|
4
|
+
attachment_presence: "no puede estar vacío"
|
|
5
|
+
attachment_content_type: "los formatos permitidos son: %{types}"
|
|
6
|
+
attachment_size_less_than: "debe pesar menos de %{count}"
|
|
7
|
+
attachment_size_greater_than: "debe pesar más de %{count}"
|
|
8
|
+
attachment_size_in: "debe pesar entre %{min} y %{max}"
|
data/config/routes.rb
ADDED
data/lib/attachs.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'attachs/active_record/base'
|
|
2
|
+
require 'attachs/magick/image'
|
|
3
|
+
require 'attachs/storages/local'
|
|
4
|
+
require 'attachs/storages/s3'
|
|
5
|
+
require 'attachs/types/file'
|
|
6
|
+
require 'attachs/types/image'
|
|
7
|
+
require 'attachs/validators/base'
|
|
8
|
+
require 'attachs/validators/attachment_content_type_validator'
|
|
9
|
+
require 'attachs/validators/attachment_presence_validator'
|
|
10
|
+
require 'attachs/validators/attachment_size_validator'
|
|
11
|
+
require 'attachs/engine'
|
|
12
|
+
require 'attachs/railtie'
|
|
13
|
+
require 'attachs/version'
|
|
14
|
+
|
|
15
|
+
module Attachs
|
|
16
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
module Attachs
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module Base
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
singleton_class.class_eval do
|
|
8
|
+
[:file, :image].each do |type|
|
|
9
|
+
name = :"has_attached_#{type}"
|
|
10
|
+
define_method name do |*args|
|
|
11
|
+
options = args.extract_options!
|
|
12
|
+
options[:type] = type
|
|
13
|
+
define_attachment *args.append(options)
|
|
14
|
+
end
|
|
15
|
+
alias_method :"attached_#{type}", name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
protected
|
|
21
|
+
|
|
22
|
+
attr_reader :stored_attachments, :deleted_attachments
|
|
23
|
+
|
|
24
|
+
def attachments
|
|
25
|
+
@attachments ||= {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def build_attachment_instance(source, options)
|
|
29
|
+
klass = options.has_key?(:type) ? options[:type].to_s.classify : 'File'
|
|
30
|
+
Attachs::Types.const_get(klass).new source, options
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def check_changed_attachments
|
|
34
|
+
@stored_attachments = []
|
|
35
|
+
@deleted_attachments = []
|
|
36
|
+
self.class.attachments.each do |attr, options|
|
|
37
|
+
if changed_attributes.has_key? attr.to_s
|
|
38
|
+
stored = attributes[attr.to_s]
|
|
39
|
+
deleted = changed_attributes[attr.to_s]
|
|
40
|
+
add_changed_attachment stored, options, :stored if stored.present?
|
|
41
|
+
add_changed_attachment deleted, options, :deleted if deleted.present?
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def iterate_attachments
|
|
47
|
+
self.class.attachments.each do |attr, options|
|
|
48
|
+
next unless instance = send(attr)
|
|
49
|
+
yield instance
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def add_changed_attachment(source, options, type)
|
|
54
|
+
(type == :stored ? stored_attachments : deleted_attachments) << (source.is_a?(String) ? build_attachment_instance(source, options) : source)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def store_attachments
|
|
58
|
+
iterate_attachments { |a| a.store }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def delete_attachments
|
|
62
|
+
iterate_attachments { |a| a.delete }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def remove_stored_attachments
|
|
66
|
+
stored_attachments.each { |a| a.delete }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def remove_deleted_attachments
|
|
70
|
+
deleted_attachments.each { |a| a.delete }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
module ClassMethods
|
|
74
|
+
|
|
75
|
+
attr_reader :attachments
|
|
76
|
+
|
|
77
|
+
def inherited(subclass)
|
|
78
|
+
subclass.instance_variable_set(:@attachments, @attachments)
|
|
79
|
+
super
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def attachable?
|
|
83
|
+
attachments.present?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
protected
|
|
87
|
+
|
|
88
|
+
def define_attachment(*args)
|
|
89
|
+
options = args.extract_options!
|
|
90
|
+
make_attachable unless attachable?
|
|
91
|
+
args.each do |attr|
|
|
92
|
+
define_attachable_attribute_methods attr.to_sym, options
|
|
93
|
+
define_default_validations attr.to_sym, options
|
|
94
|
+
attachments[attr.to_sym] = options
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def make_attachable
|
|
99
|
+
before_save :store_attachments, :check_changed_attachments
|
|
100
|
+
after_save :remove_deleted_attachments
|
|
101
|
+
before_destroy :delete_attachments
|
|
102
|
+
after_rollback :remove_stored_attachments
|
|
103
|
+
@attachments = {}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def define_default_validations(attr, options)
|
|
107
|
+
default_validations = Rails.application.config.attachs.default_validations[options[:type]]
|
|
108
|
+
validates attr, default_validations.dup if default_validations.present?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def define_attachable_attribute_methods(attr, options)
|
|
112
|
+
['set','get'].each { |method| send "define_attachable_attribute_method_#{method}", attr, options }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def define_attachable_attribute_method_set(attr, options)
|
|
116
|
+
define_method "#{attr}=" do |value|
|
|
117
|
+
if value.is_a? ActionDispatch::Http::UploadedFile or value.is_a? Rack::Test::UploadedFile or (value.is_a? String and value =~ /^[a-zA-Z0-9_-]+\.[a-zA-z]+$/)
|
|
118
|
+
attachment = build_attachment_instance(value, options)
|
|
119
|
+
attachments[attr] = attachment
|
|
120
|
+
write_attribute attr, attachment.filename
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
define_method "delete_#{attr}=" do |value|
|
|
124
|
+
if attribute_present? attr and value == '1'
|
|
125
|
+
send "#{attr}_will_change!"
|
|
126
|
+
attachments[attr] = nil
|
|
127
|
+
attributes[attr.to_s] = nil
|
|
128
|
+
end
|
|
129
|
+
instance_variable_set("@delete_#{attr}".to_sym, value)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def define_attachable_attribute_method_get(attr, options)
|
|
134
|
+
define_method attr do
|
|
135
|
+
return attachments[attr] if attachments.has_key? attr
|
|
136
|
+
return nil if super().nil? and not options.has_key? :default
|
|
137
|
+
attachments[attr] = build_attachment_instance(super(), options)
|
|
138
|
+
end
|
|
139
|
+
attr_reader :"delete_#{attr}"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Attachs
|
|
2
|
+
module Magick
|
|
3
|
+
class Image
|
|
4
|
+
|
|
5
|
+
def initialize(source, output=nil)
|
|
6
|
+
@source = source
|
|
7
|
+
@output = output
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def convert(args)
|
|
11
|
+
tokens = [convert? ? 'convert' : 'mogrify']
|
|
12
|
+
file_to_tokens tokens
|
|
13
|
+
args_to_tokens args, tokens
|
|
14
|
+
tokens << "\"#{output}\"" if convert?
|
|
15
|
+
success, output = run(tokens)
|
|
16
|
+
if block_given?
|
|
17
|
+
yield success, output
|
|
18
|
+
else
|
|
19
|
+
success
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def identify(args)
|
|
24
|
+
tokens = ['identify']
|
|
25
|
+
args_to_tokens args, tokens
|
|
26
|
+
file_to_tokens tokens
|
|
27
|
+
success, output = run(tokens)
|
|
28
|
+
if block_given?
|
|
29
|
+
yield success, output
|
|
30
|
+
else
|
|
31
|
+
success
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def dimensions
|
|
36
|
+
identify(format: '%wx%h') do |success, output|
|
|
37
|
+
success ? output.chomp.split('x').map(&:to_i) : []
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def width
|
|
42
|
+
dimensions[0]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def height
|
|
46
|
+
dimensions[1]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def resize_to_fill(max_width, max_height)
|
|
50
|
+
width, height = dimensions
|
|
51
|
+
scale = [max_width.to_f/width, max_height.to_f/height].max
|
|
52
|
+
convert resize: "#{(scale*width).ceil}x#{(scale*height).ceil}", gravity: 'center', crop: "#{max_width}x#{max_height}+0+0"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def resize_to_fit(max_width, max_height)
|
|
56
|
+
width, height = dimensions
|
|
57
|
+
scale = [max_width.to_f/width, max_height.to_f/height].reject{ |s| s==0 }.min
|
|
58
|
+
convert resize: "#{(scale*width).ceil}x#{(scale*height).ceil}", gravity: 'center'
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
protected
|
|
62
|
+
|
|
63
|
+
attr_reader :source, :output
|
|
64
|
+
|
|
65
|
+
def convert?
|
|
66
|
+
!(output.nil? and ::File.exists?(output))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def file_to_tokens(tokens)
|
|
70
|
+
tokens << (convert? ? "\"#{source}\"" : output)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def args_to_tokens(args, tokens)
|
|
74
|
+
args.each { |k, v| tokens << "-#{k} #{v}" }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def run(tokens)
|
|
78
|
+
output = `#{tokens.join(' ')}`
|
|
79
|
+
success = $?.exitstatus == 0
|
|
80
|
+
[success, output]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|