smartthumbs 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +63 -0
- data/Rakefile +2 -0
- data/app/controllers/thumbs_controller.rb +35 -0
- data/app/helpers/smartthumbs_helper.rb +7 -0
- data/config/routes.rb +3 -0
- data/lib/smartthumbs/engine.rb +13 -0
- data/lib/smartthumbs/thumbable.rb +141 -0
- data/lib/smartthumbs/version.rb +3 -0
- data/lib/smartthumbs.rb +31 -0
- data/lib/tasks/smartthumbs.rake +15 -0
- data/smartthumbs.gemspec +23 -0
- metadata +94 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Alexander Pauly
|
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.markdown
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
## Smartthumbs
|
2
|
+
|
3
|
+
This Gem helps you create any kind of thumbnails on-the-fly.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
* define your various thumb-formats inside of your model and let smartthumbs do the rest.
|
7
|
+
* writes created thumbs to the public directory - every thumb is only created once and only if needed
|
8
|
+
* Works with blob-based records and filename-based records
|
9
|
+
* can be implemented in several models
|
10
|
+
* define which strategy should be used to resize your images
|
11
|
+
|
12
|
+
## Install
|
13
|
+
Add the Gem and then create an initializer-script in config/initializers:
|
14
|
+
|
15
|
+
Smartthumbs::Config.run do |config|
|
16
|
+
config[:valid_classes] = ["Image"]
|
17
|
+
# optional
|
18
|
+
config[:assume_klass] = "Image"
|
19
|
+
end
|
20
|
+
|
21
|
+
Smartthumbs needs to know all classes that you want to use smartthumbs with.
|
22
|
+
Pass them as an array of strings via :valid_classes .
|
23
|
+
|
24
|
+
By default smartthumbs generates urls like "/th/<klass-name>/<format>/<record-id>.<extension>". If you only have a single class you can set the default-class and prevent this class name from appering in that url.
|
25
|
+
|
26
|
+
|
27
|
+
## Examples
|
28
|
+
|
29
|
+
### In your models
|
30
|
+
class Image < ActiveRecord::Base
|
31
|
+
smartthumbs :file => :file_path, :extension => "jpg", :formats => {
|
32
|
+
"thumb" => ["800x200", :cut, :w],
|
33
|
+
"small" => ["100x100", :fit],
|
34
|
+
"tiny" => ["15x15", :fill]
|
35
|
+
}
|
36
|
+
|
37
|
+
def file_path
|
38
|
+
"#{Rails.root}/tmp/uploads/#{self.id}.jpg"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
class Image < ActiveRecord::Base
|
45
|
+
smartthumbs :blob => :db_blob_column, :extension => :my_extension, :formats => {
|
46
|
+
"thumb" => ["800x100", :fit, :e]
|
47
|
+
}
|
48
|
+
|
49
|
+
def my_extension
|
50
|
+
"jpg"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
### In your View
|
55
|
+
|
56
|
+
<%= thumb_tag, @image, "tiny", :class => "red-border" %>
|
57
|
+
|
58
|
+
will generate something like:
|
59
|
+
|
60
|
+
<img src="/th/my_image/tiny/5.jpg" class="red-border"/>
|
61
|
+
|
62
|
+
|
63
|
+
Copyright (c) 2011 Alexander Pauly, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class ThumbsController < ApplicationController
|
2
|
+
|
3
|
+
require 'RMagick'
|
4
|
+
include Magick
|
5
|
+
|
6
|
+
def deliver_crop
|
7
|
+
klass, format, id = parse_params
|
8
|
+
|
9
|
+
@obj = klass.find(id)
|
10
|
+
@obj.create_thumb_for(format)
|
11
|
+
|
12
|
+
send_file(@obj.thumb_path_for(format), :type => 'image/jpeg', :disposition => 'inline')
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def parse_params
|
17
|
+
parsed = params[:path].split(".")[0..-2].first.split("/")
|
18
|
+
|
19
|
+
if parsed.length < 3 && Smartthumbs::Config.options[:assume_klass].present?
|
20
|
+
parsed.unshift(
|
21
|
+
Smartthumbs::Config.options[:assume_klass]
|
22
|
+
)
|
23
|
+
else
|
24
|
+
parsed[0] = parsed.first.gsub("-", "/").classify
|
25
|
+
end
|
26
|
+
|
27
|
+
unless Smartthumbs::Config.options[:valid_classes].include?(parsed[0])
|
28
|
+
raise "Bad Request"
|
29
|
+
else
|
30
|
+
parsed[0] = parsed.first.constantize
|
31
|
+
end
|
32
|
+
parsed
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'smartthumbs'
|
2
|
+
require 'rails'
|
3
|
+
require 'action_controller'
|
4
|
+
|
5
|
+
#http://www.themodestrubyist.com/2010/03/05/rails-3-plugins---part-2---writing-an-engine/
|
6
|
+
|
7
|
+
module Smartthumbs
|
8
|
+
class Engine < Rails::Engine
|
9
|
+
config.to_prepare do
|
10
|
+
ApplicationController.helper("Smartthumbs")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Smartthumbs
|
2
|
+
|
3
|
+
module Thumbable
|
4
|
+
require 'RMagick'
|
5
|
+
include Magick
|
6
|
+
|
7
|
+
# return the rmagick instance
|
8
|
+
def rmagick_img
|
9
|
+
if self.class.st_config[:blob].present?
|
10
|
+
@rmagick_img ||= Magick::ImageList.new.from_blob(
|
11
|
+
self.send(self.class.st_config[:blob])
|
12
|
+
).first
|
13
|
+
elsif self.class.st_config[:file].present?
|
14
|
+
@rmagick_img ||= Magick::ImageList.new.from_blob(
|
15
|
+
File.read(self.send(self.class.st_config[:file]))
|
16
|
+
).first
|
17
|
+
else
|
18
|
+
raise "No thumb source defined. You have to define neither :blob or :file"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# returns all possible @formats for the current image
|
23
|
+
def st_format(f)
|
24
|
+
self.class.st_config[:formats][f]
|
25
|
+
end
|
26
|
+
|
27
|
+
# returns the file extension for the current image
|
28
|
+
def st_extension
|
29
|
+
return "jpg" unless self.class.st_config[:extension].present?
|
30
|
+
if self.class.st_config[:extension].is_a?(String)
|
31
|
+
self.class.st_config[:extension]
|
32
|
+
else
|
33
|
+
self.send(self.class.st_config[:extension])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# creates the directory for a certain @format if it doesn't exist
|
38
|
+
def create_directory
|
39
|
+
dest = File.dirname(thumb_path_for(@format))
|
40
|
+
FileUtils.mkdir_p(dest) unless File.exists?(dest)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates the thumb for a certain @format
|
44
|
+
def create_thumb_for(format)
|
45
|
+
@format = format
|
46
|
+
return if st_format(@format).blank?
|
47
|
+
|
48
|
+
create_directory
|
49
|
+
|
50
|
+
method = st_format(@format)[1] || :cut
|
51
|
+
@x, @y = st_format(@format).first.split("x").map(&:to_i)
|
52
|
+
|
53
|
+
if self.respond_to?(method)
|
54
|
+
self.send(method)
|
55
|
+
end
|
56
|
+
|
57
|
+
rounding_error
|
58
|
+
rmagick_img.write(thumb_path_for(@format)) { self.quality = 80 }
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# returns the gravity for the current resizing process and
|
63
|
+
# provides some shrotcuts
|
64
|
+
def gravity
|
65
|
+
return Magick::CenterGravity unless (st_format(@format) || []).length >= 3
|
66
|
+
{
|
67
|
+
:new => Magick::NorthWestGravity,
|
68
|
+
:n => Magick::NorthGravity,
|
69
|
+
:ne => Magick::NorthEastGravity,
|
70
|
+
:w => Magick::WestGravity,
|
71
|
+
:c => Magick::CenterGravity,
|
72
|
+
:e => Magick::EastGravity,
|
73
|
+
:sw => Magick::SouthWestGravity,
|
74
|
+
:s => Magick::SouthGravity,
|
75
|
+
:se => Magick::SouthEastGravity
|
76
|
+
}[st_format(@format).last]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Does a thumb already exist?
|
80
|
+
def thumb_exists_for?(format)
|
81
|
+
File.exists?(self.thumb_path_for(format))
|
82
|
+
end
|
83
|
+
|
84
|
+
# returns the cache-path for a certain image
|
85
|
+
def thumb_path_for(format)
|
86
|
+
"#{Rails.root}/public#{thumb_url_for(format)}"
|
87
|
+
end
|
88
|
+
|
89
|
+
# return the http url to the resized image
|
90
|
+
# this one has to be route from which the image is
|
91
|
+
# availabe - otherwise the caching benefit is gone
|
92
|
+
def thumb_url_for(format)
|
93
|
+
if Smartthumbs::Config.options[:assume_klass] == self.class.to_s
|
94
|
+
"/th/#{format.to_s}/#{self.id}.#{st_extension}"
|
95
|
+
else
|
96
|
+
"/th/#{self.class.to_s.underscore.parameterize}/#{format.to_s}/#{self.id}.#{st_extension}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# resizes the image in a manner that both edges fit the needs.
|
101
|
+
# usually one of the edges is smaller than the needs afterwards
|
102
|
+
def fit
|
103
|
+
if self.needs_to_be_resized?
|
104
|
+
rmagick_img.resize_to_fit!(@x, @y)
|
105
|
+
else
|
106
|
+
rmagick_img.resize_to_fit(@x, @y)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# the same as +fit+, except the fact that the image
|
111
|
+
# get's filled up with a border
|
112
|
+
def fill
|
113
|
+
fit
|
114
|
+
rounding_error
|
115
|
+
border_x = (@x - rmagick_img.columns)/2
|
116
|
+
border_y = (@y - rmagick_img.rows)/2
|
117
|
+
|
118
|
+
rmagick_img.border!(border_x,border_y,"white")
|
119
|
+
end
|
120
|
+
|
121
|
+
# resizes and cuts the image, so it that it fits exactly
|
122
|
+
def cut
|
123
|
+
rmagick_img.crop_resized!(@x, @y, gravity)
|
124
|
+
end
|
125
|
+
|
126
|
+
# if there's just a small difference between the needs and the result,
|
127
|
+
# we'll make it fit exaclty
|
128
|
+
def rounding_error
|
129
|
+
dif = (@y-rmagick_img.rows) + (@x-rmagick_img.columns)
|
130
|
+
|
131
|
+
if dif > 0 && dif < 10 then
|
132
|
+
rmagick_img.resize!(@x, @y)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# checks whether the image needs to be resized to fit the current @format or not
|
137
|
+
def needs_to_be_resized?
|
138
|
+
rmagick_img.rows > @y || rmagick_img.columns > @x
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/smartthumbs.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Smartthumbs
|
2
|
+
require 'smartthumbs/engine' if defined?(Rails)
|
3
|
+
|
4
|
+
module ActiceRecord
|
5
|
+
module ClassMethods
|
6
|
+
attr_accessor :st_config
|
7
|
+
def smartthumbs(opts={})
|
8
|
+
self.st_config = opts
|
9
|
+
self.send(:include, Smartthumbs::Thumbable)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Config
|
19
|
+
class << self
|
20
|
+
attr_accessor :options
|
21
|
+
def run
|
22
|
+
self.options ||= {}
|
23
|
+
yield self.options
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
require "smartthumbs/thumbable"
|
29
|
+
::ActiveRecord::Base.send(:include, Smartthumbs::ActiceRecord)
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
namespace :smartthumbs do
|
2
|
+
|
3
|
+
desc "Delte all created thumbs"
|
4
|
+
task :delete_all do
|
5
|
+
`rm -rf #{Rails.root}/public/th`
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "delete all thumbs that are older than 1 day"
|
9
|
+
task :delete_old do
|
10
|
+
Dir["#{Rails.root}/public/th/**/**/*.jpg"].each do |f|
|
11
|
+
File.delete(f) if File.mtime(f) < 1.day.ago
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/smartthumbs.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "smartthumbs/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "smartthumbs"
|
7
|
+
s.version = Smartthumbs::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Alexander Pauly"]
|
10
|
+
s.email = ["railsninja@gmx.net"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/smartthumbs"
|
12
|
+
s.summary = %q{Smart on-the-fly thumbnail creation for blob or filesystem based images}
|
13
|
+
s.description = %q{Smartthumbs let's you define several different formats. By default, every image is available in any format. Once a thumb is requested, it will be generated on the fly and then be saved to the public directory. The next user who accesses the image will get the static image from disk.'}
|
14
|
+
|
15
|
+
s.rubyforge_project = "smartthumbs"
|
16
|
+
|
17
|
+
s.add_dependency 'rmagick'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smartthumbs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alexander Pauly
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-22 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rmagick
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Smartthumbs let's you define several different formats. By default, every image is available in any format. Once a thumb is requested, it will be generated on the fly and then be saved to the public directory. The next user who accesses the image will get the static image from disk.'
|
36
|
+
email:
|
37
|
+
- railsninja@gmx.net
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- Gemfile
|
47
|
+
- MIT-LICENSE
|
48
|
+
- README.markdown
|
49
|
+
- Rakefile
|
50
|
+
- app/controllers/thumbs_controller.rb
|
51
|
+
- app/helpers/smartthumbs_helper.rb
|
52
|
+
- config/routes.rb
|
53
|
+
- lib/smartthumbs.rb
|
54
|
+
- lib/smartthumbs/engine.rb
|
55
|
+
- lib/smartthumbs/thumbable.rb
|
56
|
+
- lib/smartthumbs/version.rb
|
57
|
+
- lib/tasks/smartthumbs.rake
|
58
|
+
- smartthumbs.gemspec
|
59
|
+
has_rdoc: true
|
60
|
+
homepage: http://rubygems.org/gems/smartthumbs
|
61
|
+
licenses: []
|
62
|
+
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
hash: 3
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project: smartthumbs
|
89
|
+
rubygems_version: 1.3.7
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: Smart on-the-fly thumbnail creation for blob or filesystem based images
|
93
|
+
test_files: []
|
94
|
+
|