rai 0.0.2 → 0.0.3
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/.gitignore +1 -0
- data/README.md +37 -13
- data/Rakefile +0 -8
- data/TODO.md +8 -0
- data/config.ru.sample +7 -0
- data/lib/rai.rb +46 -127
- data/lib/rai/helpers.rb +101 -0
- data/lib/rai/version.rb +1 -1
- metadata +5 -3
- data/config.ru +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbd2639a30affd2ab6bb5e765ceaba2f712227dd
|
4
|
+
data.tar.gz: 4b18403ea39077a3bae28c3bc3f515d2223fd8ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02914b17f67e9081d661d1b0f30241c0b16385847d8c29aa327695bbd741a437b7226c0391132060d19c18b5af65e25091f03ef504e29ce6796c716df7ee2dfe
|
7
|
+
data.tar.gz: 09daca9694b60a4af3d7ed8cc28ded965d155827970f5d01499e7a129505dccbd1beebafc6bb254ebf63a54bd6f81a9fceef4cc6bbde418729f8ebf154b291c0
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
# Ruby Adaptive-Images
|
2
|
-
This is a ruby port of Adaptive-Images (http://adaptive-images.com/,
|
2
|
+
This is a ruby port of Adaptive-Images (http://adaptive-images.com/,
|
3
|
+
originally by Matt Wilcox). It does not contain all features, but it does more
|
4
|
+
or less the same in the same way. It uses ImageMagick (via mini\_magick) in
|
5
|
+
stead of GD.
|
3
6
|
|
4
7
|
It runs as a sinatra application that can be mounted using Rack.
|
5
8
|
|
6
9
|
# Installation
|
7
10
|
|
8
|
-
|
11
|
+
`gem install rai`
|
9
12
|
|
10
13
|
# Configuration
|
11
14
|
|
12
15
|
Put something like this in your config.ru:
|
13
16
|
|
14
|
-
```
|
17
|
+
```ruby
|
15
18
|
require 'rai'
|
16
19
|
|
17
|
-
map '/
|
20
|
+
map '/images' do
|
18
21
|
Rai::App.set :img_path, '/path/to/your/images'
|
19
22
|
Rai::App.set :cache_path, '/path/to/your/cache'
|
20
23
|
run Rai::App
|
@@ -24,40 +27,61 @@ end
|
|
24
27
|
|
25
28
|
## Settings
|
26
29
|
|
27
|
-
### :
|
30
|
+
### :watch\_cache
|
28
31
|
Keep an eye on the cache to see if it needs updating.
|
32
|
+
|
29
33
|
Values: true/false
|
34
|
+
|
30
35
|
Default: true
|
31
36
|
|
32
37
|
### :resolutions
|
33
38
|
Resolution break-points (screen widths).
|
39
|
+
|
34
40
|
Value: array
|
35
|
-
Default: [1382, 992, 768, 480, 320]
|
36
41
|
|
37
|
-
|
42
|
+
Default: [2560, 1920, 1440, 1024, 768, 480]
|
43
|
+
|
44
|
+
### :jpg\_quality
|
38
45
|
JPG compression ratio, 0-100.
|
46
|
+
|
39
47
|
Value: integer, 0-100
|
40
|
-
|
48
|
+
|
49
|
+
Default: 85
|
41
50
|
|
42
51
|
### :sharpen
|
43
52
|
Wether image should be sharpened or not.
|
53
|
+
|
44
54
|
Value: true/false
|
55
|
+
|
45
56
|
Default: true
|
46
57
|
|
47
|
-
### :
|
58
|
+
### :cache\_max\_age
|
48
59
|
Browser cache TTL
|
60
|
+
|
49
61
|
Value: integer, seconds
|
62
|
+
|
50
63
|
Default: 60*60*24*7 (7 days)
|
51
64
|
|
52
|
-
### :
|
65
|
+
### :img\_path
|
53
66
|
Where images are placed.
|
67
|
+
|
54
68
|
Value: string, path
|
55
|
-
Default:File.join(File.dirname(__FILE__), 'images')
|
56
69
|
|
57
|
-
|
70
|
+
Default:File.join(File.dirname(__FILE__), '..', 'images')
|
71
|
+
|
72
|
+
### :cache\_path
|
58
73
|
Where cached versions are placed.
|
74
|
+
|
59
75
|
Value: string, path
|
60
|
-
|
76
|
+
|
77
|
+
Default: File.join(File.dirname(__FILE__), '..', 'images', 'rai-cache')
|
78
|
+
|
79
|
+
### :cookie\_name
|
80
|
+
The name of the resolution cookie.
|
81
|
+
|
82
|
+
Value: string
|
83
|
+
|
84
|
+
Default: rai-resolution
|
61
85
|
|
62
86
|
# Author
|
63
87
|
Kim Nørgaard <jasen@jasen.dk>
|
data/Rakefile
CHANGED
data/TODO.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# TODO
|
2
|
+
√ delete cookie if invalid
|
3
|
+
√ option to disable caching via parameter or route (nocache=1)
|
4
|
+
- maybe md5-sum for cache path (md5(request_path))
|
5
|
+
- transparent gift, transparent pngs
|
6
|
+
- check up on cookie-stuff - https://github.com/MattWilcox/Adaptive-Images/pull/139
|
7
|
+
- android pixel density - https://github.com/MattWilcox/Adaptive-Images/issues/99
|
8
|
+
- http client hints: https://github.com/MattWilcox/Adaptive-Images/pull/128
|
data/config.ru.sample
ADDED
data/lib/rai.rb
CHANGED
@@ -1,146 +1,65 @@
|
|
1
|
-
require 'sinatra'
|
1
|
+
require 'sinatra/base'
|
2
2
|
require 'mini_magick'
|
3
3
|
require 'fileutils'
|
4
4
|
|
5
|
+
require_relative 'rai/helpers'
|
6
|
+
|
5
7
|
module Rai
|
6
8
|
class App < Sinatra::Base
|
7
|
-
#
|
9
|
+
# Keep an eye on the cache to see if it needs updating
|
8
10
|
set :watch_cache, true
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
#
|
11
|
+
|
12
|
+
# Resolution break-points (screen widths)
|
13
|
+
set :resolutions, [2560, 1920, 1440, 1024, 768, 480]
|
14
|
+
|
15
|
+
# JPG compression ratio, 0-100
|
16
|
+
set :jpg_quality, 85
|
17
|
+
|
18
|
+
# Sharpen images after resize
|
14
19
|
set :sharpen, true
|
15
|
-
|
20
|
+
|
21
|
+
# Cache Time-To-Live
|
16
22
|
set :cache_max_age, 60*60*24*7
|
17
|
-
# where images are placed
|
18
|
-
set :img_path, File.join(File.dirname(__FILE__), 'images')
|
19
|
-
# where cached versions are placed
|
20
|
-
set :cache_path, File.join(File.dirname(__FILE__), 'images', 'cache')
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
not_found unless File.exists? @img_file
|
25
|
-
@cache_file = File.join(settings.cache_path, @resolution.to_s, @request_path)
|
26
|
-
@extension = params[:captures][0].downcase
|
24
|
+
# Image location
|
25
|
+
set :img_path, File.join(File.dirname(__FILE__), '..', 'images')
|
27
26
|
|
28
|
-
|
27
|
+
# Cache location
|
28
|
+
set :cache_path, File.join(File.dirname(__FILE__), '..', 'images', 'rai-cache')
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
else
|
33
|
-
update_cached_image
|
34
|
-
end
|
35
|
-
send_file @cache_file
|
36
|
-
end
|
30
|
+
# Cookie name
|
31
|
+
set :cookie_name, 'rai-resolution'
|
37
32
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
# Supported image types
|
34
|
+
set :supported_image_types, ['jpg', 'jpeg', 'gif', 'png']
|
35
|
+
|
36
|
+
helpers Rai::Helpers
|
37
|
+
|
38
|
+
get %r{.*?(\.(?<image_type>[\w]+))?$} do
|
39
|
+
halt(406, "No image type/extension given") unless params[:image_type]
|
40
|
+
halt(406, "Unsupported image type: #{params[:image_type]}") unless settings.supported_image_types.include? params[:image_type]
|
41
|
+
|
42
|
+
@request_path = URI.decode(request.path_info)
|
43
|
+
@resolution = get_resolution
|
44
|
+
@requested_image = File.join(settings.img_path, @request_path)
|
45
|
+
not_found unless File.file? @requested_image
|
46
|
+
@cached_image = File.join(settings.cache_path, @resolution.to_s, @request_path)
|
47
|
+
|
48
|
+
# Just send the original image if the nocache parameter is set to 1
|
49
|
+
send_file @requested_image if params[:nocache] == '1'
|
50
|
+
|
51
|
+
# Generate image or use cached version
|
52
|
+
update_image
|
53
|
+
|
54
|
+
# Set caching headers
|
55
|
+
expires settings.cache_max_age, :private, :must_revalidate
|
56
|
+
last_modified File.mtime(@requested_image)
|
43
57
|
|
44
|
-
|
45
|
-
def get_resolution
|
46
|
-
get_resolution_from_cookie || get_resolution_from_user_agent
|
47
|
-
end
|
48
|
-
|
49
|
-
def get_resolution_from_cookie
|
50
|
-
if res_cookie = request.cookies['resolution']
|
51
|
-
cookie_data = res_cookie.split(',')
|
52
|
-
total_width = client_width = cookie_data[0].to_i
|
53
|
-
# the device's pixel density factor (physical pixels per CSS pixel)
|
54
|
-
pixel_density = cookie_data[1] ? cookie_data[1].to_i : 1
|
55
|
-
# by default use the largest supported break-point
|
56
|
-
resolution = settings.resolutions.first
|
57
|
-
|
58
|
-
if pixel_density != 1
|
59
|
-
total_width = client_width * pixel_density
|
60
|
-
end
|
61
|
-
|
62
|
-
resolution =
|
63
|
-
settings.resolutions.select {|res| total_width <= res}.last
|
64
|
-
resolution ||= settings.resolutions.first
|
65
|
-
|
66
|
-
if total_width > settings.resolutions.first
|
67
|
-
resolution *= pixel_density
|
68
|
-
end
|
69
|
-
|
70
|
-
return resolution
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def get_resolution_from_user_agent
|
75
|
-
is_mobile ? settings.resolutions.min : settings.resolutions.max
|
76
|
-
end
|
77
|
-
|
78
|
-
def is_mobile
|
79
|
-
request.user_agent.downcase.match(/mobile/)
|
80
|
-
end
|
81
|
-
|
82
|
-
def refresh_cache
|
83
|
-
return unless settings.watch_cache
|
84
|
-
return if File.mtime(@cache_file) >= File.mtime(@img_file)
|
85
|
-
|
86
|
-
File.delete @cache_file
|
87
|
-
update_cached_image
|
88
|
-
end
|
89
|
-
|
90
|
-
def update_cached_image
|
91
|
-
cache_dir = File.dirname(@cache_file)
|
92
|
-
begin
|
93
|
-
#Dir.mkdir(settings.cache_path, 0755) unless File.directory?(settings.cache_path)
|
94
|
-
FileUtils.mkdir_p(cache_dir, :mode => 0755) unless File.directory?(cache_dir)
|
95
|
-
rescue SystemCallError => e
|
96
|
-
halt(500, "Unable to create caching directory. #{e}")
|
97
|
-
rescue => e
|
98
|
-
halt(500, "Unable to create caching directory. #{e}")
|
99
|
-
end
|
100
|
-
|
101
|
-
begin
|
102
|
-
image = MiniMagick::Image.open(@img_file)
|
103
|
-
rescue Exception => e
|
104
|
-
halt(500, "Error loading image: #{e}")
|
105
|
-
end
|
106
|
-
|
107
|
-
# Do we need to downscale the image?
|
108
|
-
if image[:width] <= @resolution
|
109
|
-
@cache_file = @img_file
|
110
|
-
return
|
111
|
-
end
|
112
|
-
|
113
|
-
ratio = image["%[fx:w/h]"].to_f
|
114
|
-
new_width = @resolution
|
115
|
-
new_height = (new_width*ratio).ceil
|
116
|
-
|
117
|
-
if @extension == 'jpg'
|
118
|
-
image.combine_options do |c|
|
119
|
-
c.interlace 'plane'
|
120
|
-
c.quality settings.jpg_quality
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
if settings.sharpen
|
125
|
-
radius = 0.5
|
126
|
-
sigma = 0.5
|
127
|
-
amount = 1.0
|
128
|
-
threshold = 0.02
|
129
|
-
image.unsharp "#{radius}x#{sigma}+#{amount}+#{threshold}"
|
130
|
-
end
|
131
|
-
|
132
|
-
image.resize "#{new_width}x#{new_height}"
|
133
|
-
|
134
|
-
begin
|
135
|
-
image.write @cache_file
|
136
|
-
rescue Exception => e
|
137
|
-
halt(500, "Error writing cache file: #{e}")
|
138
|
-
end
|
139
|
-
end
|
58
|
+
send_file @cached_image
|
140
59
|
end
|
141
60
|
|
142
61
|
not_found do
|
143
|
-
"Not
|
62
|
+
"Not Found: #{request.path}"
|
144
63
|
end
|
145
64
|
end
|
146
65
|
end
|
data/lib/rai/helpers.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
module Rai
|
2
|
+
module Helpers
|
3
|
+
def get_resolution
|
4
|
+
settings.resolutions.sort!{|x,y| y <=> x }
|
5
|
+
get_resolution_from_cookie || get_resolution_from_user_agent
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_resolution_from_cookie
|
9
|
+
return false unless res_cookie = request.cookies[settings.cookie_name]
|
10
|
+
|
11
|
+
if res_cookie !~ /^[0-9]+[,]*[0-9\.]+$/
|
12
|
+
response.delete_cookie settings.cookie_name
|
13
|
+
else
|
14
|
+
cookie_data = res_cookie.split(',')
|
15
|
+
total_width = client_width = cookie_data[0].to_i
|
16
|
+
# the device's pixel density factor (physical pixels per CSS pixel)
|
17
|
+
pixel_density = cookie_data[1] ? cookie_data[1].to_i : 1
|
18
|
+
# by default use the largest supported break-point
|
19
|
+
resolution = settings.resolutions.first
|
20
|
+
|
21
|
+
total_width = client_width * pixel_density
|
22
|
+
|
23
|
+
resolution =
|
24
|
+
settings.resolutions.select {|res| total_width <= res}.last
|
25
|
+
resolution ||= settings.resolutions.first
|
26
|
+
|
27
|
+
if total_width > settings.resolutions.first
|
28
|
+
resolution *= pixel_density
|
29
|
+
end
|
30
|
+
|
31
|
+
return resolution
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_resolution_from_user_agent
|
36
|
+
is_mobile ? settings.resolutions.min : settings.resolutions.max
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_mobile
|
40
|
+
request.user_agent.downcase.match(/mobi|android|touch|mini/)
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_image
|
44
|
+
# Refresh the image if the cached version is too old
|
45
|
+
if File.exists? @cached_image
|
46
|
+
return unless settings.watch_cache
|
47
|
+
return if File.mtime(@cached_image) >= File.mtime(@requested_image)
|
48
|
+
File.delete @cached_image
|
49
|
+
end
|
50
|
+
|
51
|
+
begin
|
52
|
+
image = MiniMagick::Image.open(@requested_image)
|
53
|
+
rescue Exception => e
|
54
|
+
halt(500, "Error loading image: #{e}")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Do we need to downscale the image?
|
58
|
+
if image[:width] <= @resolution
|
59
|
+
@cached_image = @requested_image
|
60
|
+
return
|
61
|
+
end
|
62
|
+
|
63
|
+
cache_dir = File.dirname(@cached_image)
|
64
|
+
|
65
|
+
begin
|
66
|
+
FileUtils.mkdir_p(cache_dir, :mode => 0755) unless File.directory?(cache_dir)
|
67
|
+
rescue SystemCallError => e
|
68
|
+
halt(500, "Unable to create caching directory. #{e}")
|
69
|
+
rescue => e
|
70
|
+
halt(500, "Unable to create caching directory. #{e}")
|
71
|
+
end
|
72
|
+
|
73
|
+
ratio = image["%[fx:w/h]"].to_f
|
74
|
+
new_width = @resolution
|
75
|
+
new_height = (new_width*ratio).ceil
|
76
|
+
|
77
|
+
if params[:image_type] == 'jpg'
|
78
|
+
image.combine_options do |c|
|
79
|
+
c.interlace 'plane'
|
80
|
+
c.quality settings.jpg_quality
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
image.resize "#{new_width}x#{new_height}"
|
85
|
+
|
86
|
+
if settings.sharpen
|
87
|
+
radius = 0.5
|
88
|
+
sigma = 0.5
|
89
|
+
amount = 1.0
|
90
|
+
threshold = 0.02
|
91
|
+
image.unsharp "#{radius}x#{sigma}+#{amount}+#{threshold}"
|
92
|
+
end
|
93
|
+
|
94
|
+
begin
|
95
|
+
image.write @cached_image
|
96
|
+
rescue Exception => e
|
97
|
+
halt(500, "Error writing cache file: #{e}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/rai/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kim Nørgaard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
@@ -48,8 +48,10 @@ files:
|
|
48
48
|
- Gemfile
|
49
49
|
- README.md
|
50
50
|
- Rakefile
|
51
|
-
-
|
51
|
+
- TODO.md
|
52
|
+
- config.ru.sample
|
52
53
|
- lib/rai.rb
|
54
|
+
- lib/rai/helpers.rb
|
53
55
|
- lib/rai/version.rb
|
54
56
|
- rai.gemspec
|
55
57
|
homepage: https://github.com/KimNorgaard/rai
|
data/config.ru
DELETED