balloon 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/README.md +185 -0
- data/README_EN.md +4 -0
- data/Rakefile +1 -0
- data/balloon.gemspec +35 -0
- data/lib/balloon.rb +42 -0
- data/lib/balloon/base.rb +43 -0
- data/lib/balloon/cache.rb +44 -0
- data/lib/balloon/configuration.rb +85 -0
- data/lib/balloon/download.rb +50 -0
- data/lib/balloon/error.rb +5 -0
- data/lib/balloon/file_extension.rb +187 -0
- data/lib/balloon/http/client.rb +91 -0
- data/lib/balloon/http/response.rb +71 -0
- data/lib/balloon/load.rb +33 -0
- data/lib/balloon/locale/en.yml +7 -0
- data/lib/balloon/processing.rb +98 -0
- data/lib/balloon/storage/file.rb +62 -0
- data/lib/balloon/storage/store.rb +84 -0
- data/lib/balloon/storage/upyun.rb +91 -0
- data/lib/balloon/up.rb +100 -0
- data/lib/balloon/uploader.rb +90 -0
- data/lib/balloon/validate.rb +41 -0
- data/lib/balloon/version.rb +3 -0
- data/lib/rails/generators/balloon/config/config_generator.rb +15 -0
- data/lib/rails/generators/balloon/config/templates/balloon.yml +11 -0
- data/spec/fixtures/UPCASE.JPG +0 -0
- data/spec/fixtures/egg +0 -0
- data/spec/fixtures/invalid.jpg +1 -0
- data/spec/fixtures/kid.gif +0 -0
- data/spec/fixtures/kids.gif +0 -0
- data/spec/fixtures/word.txt +1 -0
- metadata +237 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'mini_magick'
|
2
|
+
|
3
|
+
module Balloon
|
4
|
+
module Processing
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def resize_with_string(file)
|
8
|
+
width, height = "", ""
|
9
|
+
oranginl_img = MiniMagick::Image.open(file.path)
|
10
|
+
auto_orient!(oranginl_img, file.path)
|
11
|
+
if self.respond_to?(:uploader_size) && !(store_storage.to_s == "upyun" && upyun_is_image)
|
12
|
+
uploader_size.each do |s, o|
|
13
|
+
img = MiniMagick::Image.open(file.path)
|
14
|
+
raise ProcessError, "process error" unless img
|
15
|
+
width = img[:width]
|
16
|
+
height = img[:height]
|
17
|
+
new_img = resize(img, o)
|
18
|
+
new_img.write File.join(cache_path, "#{file.basename}_#{s}.#{file.extension}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
return {width: oranginl_img[:width], height: oranginl_img[:height]}
|
22
|
+
end
|
23
|
+
|
24
|
+
def resize(image, size)
|
25
|
+
width, height, symbol = size[:width], size[:height], size[:symbol]
|
26
|
+
if !symbol.empty? || width.match(/\%/) || height.match(/\%/)
|
27
|
+
if width == height
|
28
|
+
image = shave(image)
|
29
|
+
image.resize "#{width}"
|
30
|
+
else
|
31
|
+
image.resize "#{width}x#{height}#{symbol}"
|
32
|
+
end
|
33
|
+
else
|
34
|
+
if width == height
|
35
|
+
image = shave(image)
|
36
|
+
value = (width.to_f / image[:width].to_f) * 100
|
37
|
+
image.resize "#{value}%"
|
38
|
+
else
|
39
|
+
if width.empty?
|
40
|
+
value = (height.to_f / image[:height].to_f) * 100
|
41
|
+
image.resize "#{value}%"
|
42
|
+
elsif height.empty?
|
43
|
+
value = (width.to_f / image[:width].to_f) * 100
|
44
|
+
image.resize "#{value}%"
|
45
|
+
else
|
46
|
+
image.resize "#{width}x#{height}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
return image
|
51
|
+
end
|
52
|
+
|
53
|
+
def shave(image)
|
54
|
+
w, h = image[:width], image[:height]
|
55
|
+
if w > h
|
56
|
+
shave_off = ((w - h) / 2).round
|
57
|
+
image.shave "#{shave_off}x0"
|
58
|
+
else
|
59
|
+
shave_off = ((h - w) / 2).round
|
60
|
+
image.shave "0x#{shave_off}"
|
61
|
+
end
|
62
|
+
return image
|
63
|
+
end
|
64
|
+
|
65
|
+
def auto_orient!(img, file)
|
66
|
+
if img["exif:orientation"] == "6"
|
67
|
+
img.auto_orient
|
68
|
+
img.write file
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# parseing the size string
|
73
|
+
#
|
74
|
+
# @return [Hash] the options for string
|
75
|
+
module ClassMethods
|
76
|
+
def parsing_size_string(size)
|
77
|
+
symbol = ""
|
78
|
+
symbol_regex = /[^\d|\!|\>|\<|\%|x|X]/
|
79
|
+
if size.include?('x')
|
80
|
+
if has_symbol = size.match(/[\!|\>|\<]/)
|
81
|
+
symbol = has_symbol[0]
|
82
|
+
size_option = size[0, 7].split("x")
|
83
|
+
else
|
84
|
+
size_option = size.split("x")
|
85
|
+
end
|
86
|
+
width, height = size_option
|
87
|
+
else
|
88
|
+
width, height = size, size
|
89
|
+
end
|
90
|
+
width = width || ""
|
91
|
+
width = width.match(symbol_regex) ? "" : width
|
92
|
+
height = height || ""
|
93
|
+
height = height.match(symbol_regex) ? "" : height
|
94
|
+
return { width: width, height: height, symbol: symbol }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Balloon
|
2
|
+
module Storage
|
3
|
+
class File < Balloon::Storage::Store
|
4
|
+
def store!
|
5
|
+
_store_path = store_path
|
6
|
+
|
7
|
+
if !::File.exists? _store_path
|
8
|
+
FileUtils.mkdir_p _store_path
|
9
|
+
FileUtils.chmod_R @uploader.dir_permissions.to_i, _store_path
|
10
|
+
end
|
11
|
+
|
12
|
+
original_file = set_upload_name
|
13
|
+
store_original_file = ::File.join _store_path, original_file
|
14
|
+
cache_original_file = ::File.join @uploader.cache_path, @uploader.info[:filename]
|
15
|
+
FileUtils.mv cache_original_file, store_original_file
|
16
|
+
|
17
|
+
if @uploader.respond_to?(:uploader_size)
|
18
|
+
@uploader.uploader_size.each do |s, o|
|
19
|
+
store_file = ::File.join _store_path, set_upload_name(s)
|
20
|
+
cache_file = ::File.join @uploader.cache_path, @uploader.info[:basename]+ "_#{s}"+"."+ @uploader.info[:extension]
|
21
|
+
FileUtils.mv cache_file, store_file
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return { filename: original_file, basename: store_name}
|
25
|
+
end
|
26
|
+
|
27
|
+
def retrieve!(size_name = nil)
|
28
|
+
return "" if !upload_file
|
29
|
+
path = ::File.join upload_dir, store_filename(size_name)
|
30
|
+
return "/" + path if @uploader.asset_host.nil?
|
31
|
+
@uploader.asset_host + "/" + path
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete!
|
35
|
+
return false if !upload_file
|
36
|
+
path = ::File.join store_path, store_filename
|
37
|
+
FileUtils.rm(path) if ::File.exists?(path)
|
38
|
+
if @uploader.respond_to?(:uploader_size)
|
39
|
+
@uploader.uploader_size.each do |s, o|
|
40
|
+
path = ::File.join store_path, store_filename(s)
|
41
|
+
FileUtils.rm(path) if ::File.exists?(path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def store_filename(size_name = nil)
|
49
|
+
extension = upload_file[:extension].blank? ? "" : "." + upload_file[:extension]
|
50
|
+
if size_name.nil?
|
51
|
+
upload_file[:basename] + extension
|
52
|
+
else
|
53
|
+
upload_file[:basename] + "_" + size_name.to_s + extension
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def store_path
|
58
|
+
::File.expand_path ::File.join(@uploader.root, @uploader.store_dir, upload_dir)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Balloon
|
2
|
+
module Storage
|
3
|
+
class Store
|
4
|
+
def initialize(uploader)
|
5
|
+
@uploader = uploader
|
6
|
+
end
|
7
|
+
|
8
|
+
def store!; end
|
9
|
+
|
10
|
+
def retrieve!(size_name = nil); end
|
11
|
+
|
12
|
+
def upload_file
|
13
|
+
file_info = @uploader.info
|
14
|
+
return {} if file_info.nil?
|
15
|
+
basename = file_info[:basename] || ""
|
16
|
+
extension = file_info[:extension] || ""
|
17
|
+
{ basename: basename, extension: extension }
|
18
|
+
end
|
19
|
+
|
20
|
+
def upload_dir
|
21
|
+
@uploader.respond_to?(:uploader_dir) ? @uploader.uploader_dir : @uploader.uploader_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def store_name
|
25
|
+
return upload_file[:basename] if !@uploader.respond_to?(:uploader_name_format)
|
26
|
+
name_format = @uploader.uploader_name_format
|
27
|
+
name = name_format[:name]
|
28
|
+
if name_format[:format].to_s == "downcase"
|
29
|
+
name = name.downcase
|
30
|
+
elsif name_format[:format].to_s == "upcase"
|
31
|
+
name = name.upcase
|
32
|
+
else
|
33
|
+
name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_upload_name(size_name = nil )
|
38
|
+
if size_name
|
39
|
+
store_name + "_#{size_name.to_s}" + "." + upload_file[:extension]
|
40
|
+
else
|
41
|
+
store_name + "." + upload_file[:extension]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def connection
|
46
|
+
options = self.class.get_option(self)
|
47
|
+
basic = self.class.get_basic_auth(self)
|
48
|
+
token = self.class.token_auth
|
49
|
+
conn = Http::Client.new(options[:url]) do |builder|
|
50
|
+
builder.headers = options[:headers]
|
51
|
+
builder.basic_auth(basic[:user], basic[:password]) if !basic.nil?
|
52
|
+
builder.token_auth(self, token) if !token.nil?
|
53
|
+
end
|
54
|
+
return conn
|
55
|
+
end
|
56
|
+
|
57
|
+
class << self
|
58
|
+
def conn_option(&block)
|
59
|
+
return @option_proc unless block_given?
|
60
|
+
@option_proc = block
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_option(klass)
|
64
|
+
klass.instance_eval(&conn_option)
|
65
|
+
end
|
66
|
+
|
67
|
+
def basic_auth(&block)
|
68
|
+
return @basic_proc unless block_given?
|
69
|
+
@basic_proc = block
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_basic_auth(klass)
|
73
|
+
return nil if basic_auth.nil?
|
74
|
+
klass.instance_eval(&basic_auth)
|
75
|
+
end
|
76
|
+
|
77
|
+
def token_auth(&block)
|
78
|
+
return @token_proc unless block_given?
|
79
|
+
@token_proc = block
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Balloon
|
2
|
+
module Storage
|
3
|
+
class Upyun < Balloon::Storage::Store
|
4
|
+
|
5
|
+
conn_option do
|
6
|
+
{
|
7
|
+
url: conn_url,
|
8
|
+
headers: conn_headers,
|
9
|
+
timeout: @uploader.upyun_timeout
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
token_auth do |method, path, size, date|
|
14
|
+
"encode_canonical('#{method}', '#{path}', #{size}, '#{date}')"
|
15
|
+
end
|
16
|
+
|
17
|
+
def store!
|
18
|
+
_store_path = store_path
|
19
|
+
original_file = set_upload_name
|
20
|
+
store_original_file = ::File.join _store_path, original_file
|
21
|
+
cache_original_file = ::File.join @uploader.cache_path, @uploader.info[:filename]
|
22
|
+
file = ::File.new cache_original_file
|
23
|
+
response = connection.put(store_original_file, file.read, file.size)
|
24
|
+
raise "Connection errors" if response.nil?
|
25
|
+
|
26
|
+
if !@uploader.upyun_is_image && @uploader.respond_to?(:uploader_size)
|
27
|
+
@uploader.uploader_size.each do |s, o|
|
28
|
+
store_file = ::File.join _store_path, set_upload_name(s)
|
29
|
+
cache_file = ::File.join @uploader.cache_path, @uploader.info[:basename]+ "_#{s}"+"."+ @uploader.info[:extension]
|
30
|
+
file = ::File.new cache_file
|
31
|
+
connection.put(store_file, file.read, file.size)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
return { filename: original_file, basename: store_name }
|
36
|
+
end
|
37
|
+
|
38
|
+
def retrieve!(size_name = nil)
|
39
|
+
if !@uploader.upyun_is_image || size_name.nil?
|
40
|
+
path = ::File.join upload_dir, store_filename(size_name)
|
41
|
+
@uploader.upyun_domain + "/" + path
|
42
|
+
else
|
43
|
+
filename = store_filename + @uploader.upyun_thumb_symbol + size_name.to_s
|
44
|
+
path = ::File.join upload_dir, filename
|
45
|
+
@uploader.upyun_domain + "/" + path
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete!
|
50
|
+
_store_path = store_path
|
51
|
+
store_original_file = ::File.join _store_path, store_filename
|
52
|
+
response = connection.delete(store_original_file)
|
53
|
+
if !@uploader.upyun_is_image && @uploader.respond_to?(:uploader_size)
|
54
|
+
@uploader.uploader_size.each do |s, o|
|
55
|
+
store_file = ::File.join _store_path, store_filename(s)
|
56
|
+
connection.delete(store_file)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def encode_canonical(method, path, size, date)
|
64
|
+
options = []
|
65
|
+
options << method
|
66
|
+
options << path
|
67
|
+
options << date
|
68
|
+
options << size
|
69
|
+
options << "#{Digest::MD5.hexdigest(@uploader.upyun_password)}"
|
70
|
+
"UpYun #{@uploader.upyun_username}:#{Digest::MD5.hexdigest(options.join("&"))}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def store_filename(size_name = nil)
|
74
|
+
return upload_file[:basename] + "." + upload_file[:extension] if size_name == nil
|
75
|
+
upload_file[:basename] + "_" + size_name.to_s + "." + upload_file[:extension]
|
76
|
+
end
|
77
|
+
|
78
|
+
def conn_url
|
79
|
+
@uploader.upyun_api_host || "http://v0.api.upyun.com"
|
80
|
+
end
|
81
|
+
|
82
|
+
def conn_headers
|
83
|
+
@uploader.upyun_headers.merge({ 'Mkdir' => 'true', 'Expect' => '', 'Date' => Time.now.httpdate })
|
84
|
+
end
|
85
|
+
|
86
|
+
def store_path
|
87
|
+
::File.join("/", @uploader.upyun_bucket, upload_dir)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/balloon/up.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
module Balloon
|
2
|
+
module Up
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
include Balloon::Uploader
|
7
|
+
include Balloon::Validate
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def uploader(name, db = nil)
|
12
|
+
|
13
|
+
before_create :uploader_save
|
14
|
+
|
15
|
+
class_eval <<-RUBY
|
16
|
+
def #{name}=(file)
|
17
|
+
save_to_cache(file)
|
18
|
+
rescue DownloadError => e
|
19
|
+
@download_error = e
|
20
|
+
rescue ProcessError => e
|
21
|
+
@process_error = e
|
22
|
+
end
|
23
|
+
|
24
|
+
def #{name}
|
25
|
+
@info
|
26
|
+
end
|
27
|
+
|
28
|
+
def uploader_save
|
29
|
+
return if info.nil?
|
30
|
+
set_storage_engine
|
31
|
+
store_info = storage_engine.store!
|
32
|
+
@info[:filename] = store_info[:filename]
|
33
|
+
@info[:basename] = store_info[:basename]
|
34
|
+
end
|
35
|
+
|
36
|
+
def uploader_name
|
37
|
+
"#{name}".pluralize
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
|
41
|
+
set_keyword if db
|
42
|
+
|
43
|
+
validates_download_of name
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_keyword
|
47
|
+
if defined?(MongoMapper)
|
48
|
+
key :file_name, String
|
49
|
+
key :width, Integer
|
50
|
+
key :height, Integer
|
51
|
+
key :content_type, String
|
52
|
+
key :file_size, Integer
|
53
|
+
key :storage, String
|
54
|
+
key :created_at
|
55
|
+
elsif defined?(Mongoid)
|
56
|
+
field :file_name, type: String
|
57
|
+
field :width, type: Integer
|
58
|
+
field :height, type: Integer
|
59
|
+
field :content_type, type: String
|
60
|
+
field :file_size, type: String
|
61
|
+
field :storage, type: String
|
62
|
+
field :created_at
|
63
|
+
end
|
64
|
+
|
65
|
+
before_create :save_db
|
66
|
+
after_destroy :uploader_delete
|
67
|
+
|
68
|
+
class_eval <<-RUBY
|
69
|
+
def save_db
|
70
|
+
return if info.nil?
|
71
|
+
self.file_name = info[:filename]
|
72
|
+
self.content_type = info[:mime_type]
|
73
|
+
self.file_size = info[:size]
|
74
|
+
self.storage = store_storage.to_s
|
75
|
+
self.width = info[:width]
|
76
|
+
self.height = info[:height]
|
77
|
+
end
|
78
|
+
|
79
|
+
def url(size_name = nil)
|
80
|
+
return "" if !respond_to?(:file_name) || file_name.nil?
|
81
|
+
extension = self.file_name.to_s.match(%r"(?!\\.{1})\\w{2,}$")
|
82
|
+
basename = self.file_name.to_s.gsub(%r"\\.{1}\\w{2,}$",'')
|
83
|
+
@info = { basename: basename, extension: extension.to_s }
|
84
|
+
set_storage_engine
|
85
|
+
storage_engine.retrieve!(size_name)
|
86
|
+
end
|
87
|
+
|
88
|
+
def uploader_delete
|
89
|
+
return if !respond_to?(:file_name) || file_name.nil?
|
90
|
+
extension = self.file_name.to_s.match(%r"(?!\\.{1})\\w{2,}$")
|
91
|
+
basename = self.file_name.to_s.gsub(%r"\\.{1}\\w{2,}$",'')
|
92
|
+
@info = { basename: basename, extension: extension.to_s }
|
93
|
+
set_storage_engine
|
94
|
+
storage_engine.delete!
|
95
|
+
end
|
96
|
+
RUBY
|
97
|
+
end
|
98
|
+
end #ClassMethods
|
99
|
+
end #Up
|
100
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Balloon
|
2
|
+
module Uploader
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
include Balloon::Configuration
|
7
|
+
include Balloon::Processing
|
8
|
+
include Balloon::Cache
|
9
|
+
include Balloon::Download
|
10
|
+
attr_accessor :file
|
11
|
+
attr_reader :storage_engine
|
12
|
+
attr_reader :info
|
13
|
+
attr_accessor :download_error, :process_error
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_storage_engine
|
17
|
+
@storage_engine = eval(Balloon::Base::STORAGE_EGINE[store_storage.to_sym]).new(self) if !respond_to?(:@storage_engine)
|
18
|
+
end
|
19
|
+
|
20
|
+
def save_to_cache(up_file)
|
21
|
+
uploader_file = up_file.is_a?(String) && up_file.include?("://") ? down_url(up_file) : up_file
|
22
|
+
|
23
|
+
uploader_file_ext = Balloon::FileExtension.new(uploader_file)
|
24
|
+
file_mime_type = uploader_file_ext.mime_type
|
25
|
+
|
26
|
+
if self.respond_to?(:uploader_mimetype_white)
|
27
|
+
if !uploader_mimetype_white.include?(file_mime_type)
|
28
|
+
raise Balloon::DownloadError, I18n.translate(:"errors.messages.down_mime_error")
|
29
|
+
end
|
30
|
+
elsif self.respond_to?(:uploader_mimetype_black)
|
31
|
+
if !uploader_mimetype_black.include?(file_mime_type)
|
32
|
+
raise Balloon::DownloadError, I18n.translate(:"errors.messages.down_mime_error")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
generate_cache_directory
|
37
|
+
up_file = uploader_file_ext.save_to cache_path, permissions
|
38
|
+
uploader_file = up_file
|
39
|
+
img = resize_with_string up_file
|
40
|
+
@info = {
|
41
|
+
width: img[:width],
|
42
|
+
height: img[:height],
|
43
|
+
size: up_file.size,
|
44
|
+
mime_type: up_file.mime_type,
|
45
|
+
filename: up_file.filename,
|
46
|
+
basename: up_file.basename,
|
47
|
+
extension: up_file.extension
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def url(size_name = nil); end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
def uploader_dir(name)
|
55
|
+
define_method "uploader_dir" do; name; end
|
56
|
+
end
|
57
|
+
|
58
|
+
def uploader_size(options)
|
59
|
+
list = {}
|
60
|
+
|
61
|
+
if options.is_a?(Hash)
|
62
|
+
options.each do |key, value|
|
63
|
+
list[key] = parsing_size_string(value)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
list[:orignal] = parsing_size_string options
|
67
|
+
end
|
68
|
+
|
69
|
+
define_method "uploader_size" do; list; end
|
70
|
+
end
|
71
|
+
|
72
|
+
def uploader_name_format(info)
|
73
|
+
define_method "uploader_name_format" do
|
74
|
+
name = info[:name].nil? ? info[:name] : info[:name].call(self)
|
75
|
+
{ name: name, format: info[:format] }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def uploader_mimetype_white(list)
|
80
|
+
raise "just choise one method" if respond_to?(:uploader_mime_type_black)
|
81
|
+
define_method "uploader_mimetype_white" do; list; end
|
82
|
+
end
|
83
|
+
|
84
|
+
def uploader_mimetype_black(list)
|
85
|
+
raise "just choise one method" if respond_to?(:uploader_mime_type_black)
|
86
|
+
define_method "uploader_mimetype_black" do; list; end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end #Uploader
|
90
|
+
end
|