photo-cook 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6607cf1acfc43a8d2f3ec8877856f3f0e947726d
4
+ data.tar.gz: 21d67faa51dd1040a7c22250af93d20b5e61d51d
5
+ SHA512:
6
+ metadata.gz: 8d3fcf83ba40a11f12940f229e9ce7d5b0b94ae5530360bf23e0453cdff7a31bc9f1e465a7ce24181b9d9858d93af28b51fbcbe1f4bbc457728ccacd960183d1
7
+ data.tar.gz: 1f25bb170da95d5bf5275ea7aa6eb42ead54c531cba7527dc388ed65349c7267a946f5fd1d58d46543d4842580e99d2ac3a106790ea0f875d9507716b928b79d
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Declare your gem's dependencies in photo_cook.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
7
+
8
+ # Declare any dependencies that are still in development here instead of in
9
+ # your gemspec. These might include edge Rails or gems from your path or
10
+ # Git. Remember to move these dependencies to your gemspec before releasing
11
+ # your gem to rubygems.org.
12
+
13
+ # To use debugger
14
+ # gem 'debugger'
15
+
16
+ gem 'mini_magick'
data/Gemfile.lock ADDED
@@ -0,0 +1,19 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ photo-cook (1.0.0)
5
+ mini_magick (~> 4.0)
6
+ rack (~> 1.5)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ mini_magick (4.1.0)
12
+ rack (1.5.5)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ mini_magick
19
+ photo-cook!
data/Rakefile ADDED
@@ -0,0 +1,32 @@
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 = 'PhotoCook'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -0,0 +1,9 @@
1
+ module PhotoCook
2
+ module CarrierWave
3
+
4
+ def resize(*args)
5
+ PhotoCook.resize(url, *args)
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module PhotoCook
2
+ class Engine < ::Rails::Engine
3
+
4
+ initializer :javascripts do |app|
5
+ app.config.assets.paths << File.join(PhotoCook::Engine.root, 'app/assets/javascripts')
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module PhotoCook
2
+ class MagickPhoto < MiniMagick::Image
3
+ attr_accessor :source_path
4
+ attr_accessor :resized_path
5
+ end
6
+ end
@@ -0,0 +1,107 @@
1
+ # To use this middleware you should configure application:
2
+ # application.config.middleware.insert_before(Rack::Sendfile, PhotoCook::Middleware, Rails.root)
3
+ require 'rack'
4
+
5
+ module PhotoCook
6
+ class Middleware
7
+
8
+ def initialize(app, root)
9
+ @app, @root = app, root
10
+ end
11
+
12
+ # The example will be concentrated around uri:
13
+ # /uploads/photos/resized/car-640x320crop.png
14
+ def call(env)
15
+ uri = extract_uri(env)
16
+
17
+ return default_actions(env) unless
18
+
19
+ # Lets ensure that directory matches PhotoCook.resize_dir
20
+ # dir = /uploads/photos/resized
21
+ # File::SEPARATOR + PhotoCook.resize_dir = /resized
22
+ # dir.chomp! = /uploads/photos
23
+ (dir = File.dirname(uri)).chomp!(File::SEPARATOR + PhotoCook.resize_dir) &&
24
+
25
+ # Lets ensure that photo_basename ends with resize command
26
+ # photo_name = car-640x320crop.png
27
+ # photo_basename = car-640x320crop
28
+ # photo_basename.sub! = car.png
29
+ (photo_name = File.basename(uri)) &&
30
+ (photo_basename = File.basename(photo_name, '.*')).sub!(command_regex, '')
31
+
32
+ return default_actions(env) if requested_file_exists?(uri)
33
+
34
+ # Regex match: _640x320crop
35
+ command = Regexp.last_match
36
+
37
+ # At this point we are sure that this request is targeting to resize photo
38
+
39
+ # Lets assemble path of the source photo
40
+ source_path = assemble_source_path(dir, photo_basename + File.extname(photo_name))
41
+
42
+ # Finally resize photo
43
+ resizer = PhotoCook::Resizer.instance
44
+
45
+ # Resizer will store photo in resize directory
46
+ photo = resizer.resize source_path, command[:width].to_i, command[:height].to_i, !!command[:crop]
47
+
48
+ if photo
49
+ log(photo, source_path, uri, command) if defined?(Rails)
50
+
51
+ # http://rubylogs.com/writing-rails-middleware/
52
+ # https://viget.com/extend/refactoring-patterns-the-rails-middleware-response-handler
53
+ status, headers, body = Rack::File.new(File.join(@root, PhotoCook.public_dir)).call(env)
54
+ response = Rack::Response.new(body, status, headers)
55
+ response.finish
56
+ else
57
+ default_actions(env)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def assemble_source_path(dir, photo_name)
64
+ URI.decode File.join(@root, PhotoCook.public_dir, dir, photo_name)
65
+ end
66
+
67
+ def requested_file_exists?(uri)
68
+ # /my_awesome_project_root/public/uploads/photos/resized/car-640x320crop.png
69
+ File.exists? File.join(@root, PhotoCook.public_dir, uri)
70
+ end
71
+
72
+ def extract_uri(env)
73
+ Rack::Utils.unescape(env['PATH_INFO'])
74
+ end
75
+
76
+ def default_actions(env)
77
+ @app.call(env)
78
+ end
79
+
80
+ # Proportional support
81
+ # http://stackoverflow.com/questions/7200909/imagemagick-convert-to-fixed-width-proportional-height
82
+ def command_regex
83
+ unless @r_command
84
+ w = /(?<width>\d+)/
85
+ h = /(?<height>\d+)/
86
+ @r_command = %r{
87
+ \- (?:(?:#{w}x#{h}) | (?:#{w}x) | (?:x#{h})) (?<crop>crop)? \z
88
+ }x
89
+ end
90
+ @r_command
91
+ end
92
+
93
+ def log(photo, source_path, resized_path, command)
94
+ w = command[:width].to_i
95
+ h = command[:height].to_i
96
+ crop = !!command[:crop]
97
+ Rails.logger.info %{
98
+ [PhotoCook] Resized photo.
99
+ Source file: "#{source_path}".
100
+ Resized file: "#{resized_path}".
101
+ Width: #{w == 0 ? 'auto': "#{w}px"}.
102
+ Height: #{h == 0 ? 'auto': "#{h}px"}.
103
+ Crop: #{crop ? 'yes' : 'no'}.
104
+ }
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,91 @@
1
+ # Resize algorithms from
2
+ # https://github.com/carrierwaveuploader/carrierwave/blob/71cb18bba4a2078524d1ea683f267d3a97aa9bc8/lib/carrierwave/processing/rmagick.rb
3
+
4
+ module PhotoCook
5
+ class Resizer
6
+ include Singleton
7
+
8
+ CENTER_GRAVITY = 'Center'
9
+ TRANSPARENT_BACKGROUND = 'rgba(255,255,255,0.0)'
10
+ PHOTO_QUALITY = 100
11
+
12
+ def resize(photo_path, width, height, crop = false)
13
+ if crop
14
+ resize_to_fill photo_path, width, height
15
+ else
16
+ resize_to_fit photo_path, width, height
17
+ end
18
+ end
19
+
20
+ # Resize the photo to fit within the specified dimensions:
21
+ # - the original aspect ratio will be kept
22
+ # - new dimensions will be not larger then the specified
23
+ def resize_to_fit(photo_path, width, height)
24
+ # Do nothing if photo is not valid so exceptions will be not thrown
25
+ return unless (photo = open(photo_path)).try(:valid?)
26
+
27
+ photo.combine_options do |cmd|
28
+ cmd.quality PHOTO_QUALITY
29
+ cmd.resize "#{width == 0 ? nil : width}x#{height == 0 ? nil : height}"
30
+ end
31
+
32
+ store photo, PhotoCook.assemble_path(photo_path, width, height, false)
33
+ end
34
+
35
+ # Resize the photo to fit within the specified dimensions:
36
+ # - new dimensions will be the same as specified
37
+ # - the photo will be cropped if necessary
38
+ def resize_to_fill(photo_path, width, height)
39
+ # Do nothing if photo is not valid so exceptions will be not thrown
40
+ return unless (photo = open(photo_path)).try(:valid?)
41
+
42
+ cols, rows = photo[:dimensions]
43
+ photo.combine_options do |cmd|
44
+ if width != cols || height != rows
45
+ scale_x = width / cols.to_f
46
+ scale_y = height / rows.to_f
47
+ if scale_x >= scale_y
48
+ cols = (scale_x * (cols + 0.5)).round
49
+ rows = (scale_x * (rows + 0.5)).round
50
+ cmd.resize "#{cols}"
51
+ else
52
+ cols = (scale_y * (cols + 0.5)).round
53
+ rows = (scale_y * (rows + 0.5)).round
54
+ cmd.resize "x#{rows}"
55
+ end
56
+ end
57
+ cmd.gravity CENTER_GRAVITY
58
+ cmd.background TRANSPARENT_BACKGROUND
59
+ cmd.quality PHOTO_QUALITY
60
+ cmd.extent "#{width}x#{height}" if cols != width || rows != height
61
+ end
62
+
63
+ store photo, PhotoCook.assemble_path(photo_path, width, height, true)
64
+ end
65
+
66
+ private
67
+
68
+ def open(photo_path)
69
+ begin
70
+ # MiniMagick::Image.open creates a temporary file for us and protects original
71
+ photo = MagickPhoto.open(photo_path)
72
+ photo.source_path = photo_path
73
+ photo
74
+ rescue
75
+ nil
76
+ end
77
+ end
78
+
79
+ def store(resized_photo, path_to_store_at)
80
+ dir = File.dirname path_to_store_at
81
+ Dir.mkdir dir unless File.exists?(dir)
82
+ resized_photo.write path_to_store_at
83
+ resized_photo.resized_path = path_to_store_at
84
+ resized_photo
85
+ end
86
+
87
+ def normalize_dimensions(w, h)
88
+ [w.to_i == 0 ? nil : w.to_i, h.to_i == 0 ? nil : h.to_i]
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ module PhotoCook
2
+ VERSION = '1.0.0'
3
+ end
data/lib/photo-cook.rb ADDED
@@ -0,0 +1,41 @@
1
+ module PhotoCook
2
+
3
+ mattr_writer :public_dir
4
+ mattr_writer :resize_dir
5
+
6
+ def self.public_dir
7
+ @public_dir || 'public'
8
+ end
9
+
10
+ def self.resize_dir
11
+ @resize_dir || 'resized'
12
+ end
13
+
14
+ def self.resize(*args)
15
+ assemble_path(*args)
16
+ end
17
+
18
+ def self.assemble_path(path, width, height, crop = false)
19
+ File.join PhotoCook.assemble_dir(path), PhotoCook.assemble_name(path, width, height, crop)
20
+ end
21
+
22
+ def self.assemble_dir(path)
23
+ File.join File.dirname(path), PhotoCook.resize_dir
24
+ end
25
+
26
+ def self.assemble_name(path, width, height, crop = false)
27
+ File.basename(path, '.*') + PhotoCook.assemble_command(width, height, crop) + File.extname(path)
28
+ end
29
+
30
+ def self.assemble_command(width, height, crop = false)
31
+ prefix = "-#{width == 0 ? '' : width}x#{height == 0 ? '' : height}"
32
+ prefix + (crop ? 'crop' : '')
33
+ end
34
+
35
+ end
36
+
37
+ require 'photo-cook/engine' if defined?(Rails)
38
+ require 'photo-cook/resizer'
39
+ require 'photo-cook/middleware'
40
+ require 'photo-cook/carrierwave'
41
+ require 'photo-cook/magick-photo'
@@ -0,0 +1,16 @@
1
+ require File.expand_path('../lib/photo-cook/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'photo-cook'
5
+ s.version = PhotoCook::VERSION
6
+ s.authors = ['Yaroslav Konoplov']
7
+ s.email = ['yaroslav@inbox.com']
8
+ s.homepage = 'http://github.com/yivo/photo-cook'
9
+ s.summary = 'Simple solution for photo resizing'
10
+ s.description = 'This is a simple solution for photo resizing.'
11
+ s.license = 'MIT'
12
+ s.files = `git ls-files`.split("\n")
13
+
14
+ s.add_dependency 'rack', '~> 1.5'
15
+ s.add_dependency 'mini_magick', '~> 4.0'
16
+ end
@@ -0,0 +1,49 @@
1
+ window.PhotoCook = {
2
+ resizeDir: function() {
3
+ return <%= PhotoCook.resize_dir.to_json %>;
4
+ },
5
+
6
+ pixelRatio: (function() {
7
+ var mediaQuery = '(-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-resolution: 1.5dppx)';
8
+ return function() {
9
+ var ratio = window.devicePixelRatio;
10
+
11
+ // If no ratio found check if screen is retina
12
+ // and if so return 2x ratio
13
+ if (ratio == null && typeof window.matchMedia === 'function') {
14
+ if (window.matchMedia(mediaQuery).matches) {
15
+ ratio = 2;
16
+ }
17
+ }
18
+
19
+ return ratio != null ? ratio : 1;
20
+ };
21
+ })()
22
+ };
23
+
24
+ window.PhotoCook.resize = function(path, width, height, crop) {
25
+ if (path == null) { return path; }
26
+
27
+ var ratio = PhotoCook.pixelRatio();
28
+ var command = '-' + (width ? Math.round(width * ratio) : '')
29
+ + 'x'
30
+ + (height ? Math.round(height * ratio) : '')
31
+ + (crop ? 'crop' : '');
32
+
33
+ var directory = path.slice(0, Math.max(path.lastIndexOf('/'), 0));
34
+
35
+ var index = path.lastIndexOf('.');
36
+ var extension = index >= 0 ? path.slice(index) : '';
37
+
38
+ var basename = path.slice(
39
+ directory ? directory.length + 1 : 0,
40
+ extension ? 0 - extension.length : path.length
41
+ );
42
+
43
+ return (directory ? directory + '/' : directory)
44
+ + PhotoCook.resizeDir()
45
+ + '/'
46
+ + basename
47
+ + command
48
+ + extension;
49
+ };
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: photo-cook
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Yaroslav Konoplov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mini_magick
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ description: This is a simple solution for photo resizing.
42
+ email:
43
+ - yaroslav@inbox.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - Rakefile
52
+ - lib/photo-cook.rb
53
+ - lib/photo-cook/carrierwave.rb
54
+ - lib/photo-cook/engine.rb
55
+ - lib/photo-cook/magick-photo.rb
56
+ - lib/photo-cook/middleware.rb
57
+ - lib/photo-cook/resizer.rb
58
+ - lib/photo-cook/version.rb
59
+ - photo-cook.gemspec
60
+ - vendor/assets/javascripts/photo-cook/photo-cook.js.erb
61
+ homepage: http://github.com/yivo/photo-cook
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.4.8
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Simple solution for photo resizing
85
+ test_files: []