dynamic_image 0.9.5 → 0.9.6
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.
- data/README.rdoc +1 -1
- data/Rakefile +22 -22
- data/VERSION +1 -1
- data/app/controllers/images_controller.rb +64 -64
- data/app/models/image.rb +187 -187
- data/config/routes.rb +5 -5
- data/dynamic_image.gemspec +30 -36
- data/init.rb +1 -1
- data/lib/binary_storage.rb +16 -16
- data/lib/binary_storage/active_record_extensions.rb +127 -127
- data/lib/binary_storage/blob.rb +100 -100
- data/lib/dynamic_image.rb +54 -54
- data/lib/dynamic_image/active_record_extensions.rb +49 -49
- data/lib/dynamic_image/engine.rb +3 -3
- data/lib/dynamic_image/filterset.rb +72 -72
- data/lib/dynamic_image/helper.rb +102 -102
- data/lib/generators/dynamic_image/USAGE +0 -1
- data/lib/generators/dynamic_image/dynamic_image_generator.rb +27 -27
- metadata +10 -15
- data/test/dynamic_image_test.rb +0 -8
- data/test/test_helper.rb +0 -3
data/lib/dynamic_image.rb
CHANGED
@@ -13,8 +13,8 @@ require 'active_record'
|
|
13
13
|
require 'binary_storage'
|
14
14
|
|
15
15
|
if Rails::VERSION::MAJOR == 3
|
16
|
-
|
17
|
-
|
16
|
+
# Load the engine
|
17
|
+
require 'dynamic_image/engine' if defined?(Rails)
|
18
18
|
end
|
19
19
|
|
20
20
|
require 'dynamic_image/active_record_extensions'
|
@@ -22,57 +22,57 @@ require 'dynamic_image/filterset'
|
|
22
22
|
require 'dynamic_image/helper'
|
23
23
|
|
24
24
|
module DynamicImage
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
25
|
+
@@dirty_memory = false
|
26
|
+
@@page_caching = true
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
def dirty_memory=(flag)
|
31
|
+
@@dirty_memory = flag
|
32
|
+
end
|
33
|
+
|
34
|
+
def dirty_memory
|
35
|
+
@@dirty_memory
|
36
|
+
end
|
37
|
+
|
38
|
+
def page_caching=(flag)
|
39
|
+
@@page_caching = flag
|
40
|
+
end
|
41
|
+
|
42
|
+
def page_caching
|
43
|
+
@@page_caching
|
44
|
+
end
|
45
|
+
|
46
|
+
def max_size
|
47
|
+
@@max_size ||= "2000x2000"
|
48
|
+
end
|
49
|
+
|
50
|
+
def max_size=(new_max_size)
|
51
|
+
@@max_size = new_max_size
|
52
|
+
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
def crash_size
|
55
|
+
@@crash_size ||= "10000x10000"
|
56
|
+
end
|
57
|
+
|
58
|
+
def crash_size=(new_crash_size)
|
59
|
+
@@crash_size = new_crash_size
|
60
|
+
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
62
|
+
# RMagick stores image data internally, Ruby doesn't see the used memory.
|
63
|
+
# This method performs garbage collection if @@dirty_memory has been flagged.
|
64
|
+
# More details here: http://rubyforge.org/forum/message.php?msg_id=1995
|
65
|
+
def clean_dirty_memory(options={})
|
66
|
+
options.symbolize_keys!
|
67
|
+
if @@dirty_memory || options[:force]
|
68
|
+
gc_disabled = GC.enable
|
69
|
+
GC.start
|
70
|
+
GC.disable if gc_disabled
|
71
|
+
@@dirty_memory = false
|
72
|
+
true
|
73
|
+
else
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -1,60 +1,60 @@
|
|
1
1
|
require 'dynamic_image'
|
2
2
|
|
3
3
|
module DynamicImage
|
4
|
-
|
4
|
+
module ActiveRecordExtensions
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def self.included(base)
|
7
|
+
base.send :extend, ClassMethods
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
10
|
+
module ClassMethods
|
11
|
+
# By using <tt>belongs_to_image</tt> over <tt>belongs_to</tt>, you gain the ability to
|
12
|
+
# set the image directly from an uploaded file. This works exactly like <tt>belongs_to</tt>,
|
13
|
+
# except the class name will default to 'Image' - not the name of the association.
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
#
|
17
|
+
# # Model code
|
18
|
+
# class Person < ActiveRecord::Base
|
19
|
+
# belongs_to_image :mugshot
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # View code
|
23
|
+
# <% form_for 'person', @person, :html => {:multipart => true} do |f| %>
|
24
|
+
# <%= f.file_field :mugshot %>
|
25
|
+
# <% end %>
|
26
|
+
#
|
27
|
+
def belongs_to_image(association_id, options={})
|
28
|
+
options[:class_name] ||= 'Image'
|
29
|
+
options[:foreign_key] ||= options[:class_name].downcase+'_id'
|
30
|
+
belongs_to association_id, options
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
32
|
+
# Overwrite the setter method
|
33
|
+
class_eval <<-end_eval
|
34
|
+
alias_method :associated_#{association_id}=, :#{association_id}=
|
35
|
+
def #{association_id}=(img_obj)
|
36
|
+
# Convert a Tempfile to a proper Image
|
37
|
+
unless img_obj.kind_of?(ActiveRecord::Base)
|
38
|
+
DynamicImage.dirty_memory = true # Flag for GC
|
39
|
+
img_obj = Image.create(:imagefile => img_obj)
|
40
|
+
end
|
41
|
+
# Quietly skip blank strings
|
42
|
+
unless img_obj.kind_of?(String) && img_obj.blank?
|
43
|
+
self.associated_#{association_id} = img_obj
|
44
|
+
end
|
45
|
+
end
|
46
|
+
def #{association_id}?
|
47
|
+
(self.#{association_id} && self.#{association_id}.data?) ? true : false
|
48
|
+
end
|
49
|
+
end_eval
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
send :include, DynamicImage::ActiveRecordExtensions::InstanceMethods
|
52
|
+
end
|
53
|
+
end
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
module InstanceMethods
|
56
|
+
end
|
57
|
+
end
|
58
58
|
end
|
59
59
|
|
60
60
|
ActiveRecord::Base.send(:include, DynamicImage::ActiveRecordExtensions)
|
data/lib/dynamic_image/engine.rb
CHANGED
@@ -2,78 +2,78 @@ require 'dynamic_image'
|
|
2
2
|
|
3
3
|
module DynamicImage
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
5
|
+
@@filtersets = Hash.new
|
6
|
+
|
7
|
+
# Singleton methods for the filtersets hash.
|
8
|
+
class << @@filtersets
|
9
|
+
def names; keys; end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Accessor for the filtersets hash. Installed filter names are available through the <tt>names</tt> method. Example:
|
13
|
+
# @filter_names = DynamicImage.filtersets.names
|
14
|
+
def self.filtersets
|
15
|
+
@@filtersets
|
16
|
+
end
|
17
|
+
|
18
|
+
# Base class for filter sets. Extending this with your own subclasses will automatically enable them for use.
|
19
|
+
# You'll need to overwrite <tt>Filterset.process</tt> in order to make your filter useful. Note that it's a class
|
20
|
+
# method.
|
21
|
+
#
|
22
|
+
# === Example
|
23
|
+
#
|
24
|
+
# class BlogThumbnailsFilterset < DynamicImage::Filterset
|
25
|
+
# def self.process(image)
|
26
|
+
# image = image.sepiatone # convert the image to sepia tones
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# The filter set is now available for use in your application:
|
31
|
+
#
|
32
|
+
# <%= dynamic_image_tag( @blog_post.image, :size => "120x100", :filterset => 'blog_thumbnails' ) %>
|
33
|
+
#
|
34
|
+
# === Applying effects by default
|
35
|
+
#
|
36
|
+
# If <tt>Image.get_oricessed</tt> is called without filters, it will look for a set named 'default'.
|
37
|
+
# This means that you can automatically apply effects on resized images by defining a class called <tt>DefaultFilterset</tt>:
|
38
|
+
#
|
39
|
+
# class DefaultFilterset < DynamicImage::Filterset
|
40
|
+
# def self.process(image)
|
41
|
+
# image = image.unsharp_mask # apply unsharp mask on images by default.
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# === Chaining filters
|
46
|
+
#
|
47
|
+
# You can only apply one filterset on an image, but compound filters can easily be created:
|
48
|
+
#
|
49
|
+
# class CompoundFilterset < DynamicImage::Filterset
|
50
|
+
# def self.process(image)
|
51
|
+
# image = MyFirstFilterset.process(image)
|
52
|
+
# image = SomeOtherFilterset.process(image)
|
53
|
+
# image = DefaultFilterset.process(image)
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
class Filterset
|
58
|
+
include ::Magick
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
# Detect inheritance and store the new filterset in the lookup table.
|
61
|
+
def self.inherited(sub)
|
62
|
+
filter_name = sub.name.gsub!( /Filterset$/, '' ).underscore
|
63
|
+
DynamicImage.filtersets[filter_name] = sub
|
64
|
+
end
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
66
|
+
# Get a Filterset class by name. Accepts a symbol or string, CamelCase and under_scores both work.
|
67
|
+
def self.[](filter_name)
|
68
|
+
filter_name = filter_name.to_s if filter_name.kind_of? Symbol
|
69
|
+
filter_name = filter_name.underscore
|
70
|
+
DynamicImage.filtersets[filter_name] || nil
|
71
|
+
end
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
end
|
73
|
+
# Process the image. This is a dummy method, you should overwrite it in your subclass.
|
74
|
+
def self.process(image)
|
75
|
+
# This is a stub
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/dynamic_image/helper.rb
CHANGED
@@ -1,107 +1,107 @@
|
|
1
1
|
require 'dynamic_image'
|
2
2
|
|
3
3
|
module DynamicImage
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
4
|
+
module Helper
|
5
|
+
|
6
|
+
# Returns an hash consisting of the URL to the dynamic image and parsed options. This is mostly for internal use by
|
7
|
+
# dynamic_image_tag and dynamic_image_url.
|
8
|
+
def dynamic_image_options(image, options = {})
|
9
|
+
options.symbolize_keys!
|
10
|
+
|
11
|
+
options = {:crop => false}.merge(options)
|
12
|
+
url_options = {:controller => "/images", :action => :render_dynamic_image, :id => image}
|
13
|
+
|
14
|
+
if options[:original]
|
15
|
+
url_options[:original] = 'original'
|
16
|
+
options.delete(:original)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Image sizing
|
20
|
+
if options[:size]
|
21
|
+
new_size = Vector2d.new(options[:size])
|
22
|
+
image_size = Vector2d.new(image.size)
|
23
|
+
|
24
|
+
unless options[:upscale]
|
25
|
+
new_size.x = image_size.x if new_size.x > 0 && new_size.x > image_size.x
|
26
|
+
new_size.y = image_size.y if new_size.y > 0 && new_size.y > image_size.y
|
27
|
+
end
|
28
|
+
|
29
|
+
unless options[:crop]
|
30
|
+
new_size = image_size.constrain_both(new_size)
|
31
|
+
end
|
32
|
+
|
33
|
+
options[:size] = new_size.round.to_s
|
34
|
+
url_options[:size] = options[:size]
|
35
|
+
end
|
36
|
+
options.delete :crop
|
37
|
+
|
38
|
+
if options[:no_size_attr]
|
39
|
+
options.delete :no_size_attr
|
40
|
+
options.delete :size
|
41
|
+
end
|
42
|
+
|
43
|
+
# Filterset
|
44
|
+
if options[:filterset]
|
45
|
+
url_options[:filterset] = options[:filterset]
|
46
|
+
options.delete :filterset
|
47
|
+
end
|
48
|
+
|
49
|
+
# Filename
|
50
|
+
if options[:filename]
|
51
|
+
filename = options[:filename]
|
52
|
+
unless filename =~ /\.[\w]{1,4}$/
|
53
|
+
filename += "." + image.filename.split(".").last
|
54
|
+
end
|
55
|
+
url_options[:filename] = filename
|
56
|
+
else
|
57
|
+
url_options[:filename] = image.filename
|
58
|
+
end
|
59
|
+
|
60
|
+
# Alt attribute
|
61
|
+
options[:alt] ||= image.name if image.name?
|
62
|
+
options[:alt] ||= image.filename.split('.').first.capitalize
|
63
|
+
|
64
|
+
if options.has_key?(:only_path)
|
65
|
+
url_options[:only_path] = options[:only_path]
|
66
|
+
options[:only_path] = nil
|
67
|
+
end
|
68
|
+
if options.has_key?(:host)
|
69
|
+
url_options[:host] = options[:host]
|
70
|
+
options[:host] = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
{:url => url_for(url_options), :options => options}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns an image tag for the provided image model, works similar to the rails <tt>image_tag</tt> helper.
|
77
|
+
#
|
78
|
+
# The following options are supported (the rest will be forwarded to <tt>image_tag</tt>):
|
79
|
+
#
|
80
|
+
# * :size - Resize the image to fit these proportions. Size is given as a string with the format
|
81
|
+
# '100x100'. Either dimension can be omitted, for example: '100x'
|
82
|
+
# * :crop - Crop the image to the size given. (Boolean, default: <tt>false</tt>)
|
83
|
+
# * :no_size_attr - Do not include width and height attributes in the image tag. (Boolean, default: false)
|
84
|
+
# * :filterset - Apply the given filterset to the image
|
85
|
+
#
|
86
|
+
# ==== Examples
|
87
|
+
#
|
88
|
+
# dynamic_image_tag(@image) # Original image
|
89
|
+
# dynamic_image_tag(@image, :size => "100x") # Will be 100px wide
|
90
|
+
# dynamic_image_tag(@image, :size => "100x100") # Will fit within 100x100
|
91
|
+
# dynamic_image_tag(@image, :size => "100x100", :crop => true) # Will be cropped to 100x100
|
92
|
+
#
|
93
|
+
def dynamic_image_tag(image, options = {})
|
94
|
+
parsed_options = dynamic_image_options(image, options)
|
95
|
+
image_tag(parsed_options[:url], parsed_options[:options] ).gsub(/\?[\d]+/,'').html_safe
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns an url corresponding to the provided image model.
|
99
|
+
# Special options are documented in ApplicationHelper.dynamic_image_tag, only <tt>:size</tt>, <tt>:filterset</tt> and <tt>:crop</tt> apply.
|
100
|
+
def dynamic_image_url(image, options = {})
|
101
|
+
parsed_options = dynamic_image_options(image, options)
|
102
|
+
parsed_options[:url]
|
103
|
+
end
|
104
|
+
end
|
105
105
|
end
|
106
106
|
|
107
|
-
ActionView::Base.send(:include, DynamicImage::Helper)
|
107
|
+
ActionView::Base.send(:include, DynamicImage::Helper)
|