backframe 0.0.23 → 0.0.24

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 164aed2c6cdb4b3e52b7443072cad51c4942a12e
4
- data.tar.gz: a984923f33796da1f5a2667107373286b7fa87ac
3
+ metadata.gz: 4fed772ee7c50ff2200092d5650f184207b62b98
4
+ data.tar.gz: bbfa73a46d0017893199aa11d6d4c9062ada92f9
5
5
  SHA512:
6
- metadata.gz: 61d46c442596d02acee9893393a76aed47979695d1b1ca44d44dbceed7b1d7dd26fd260736c76526a6ecbedb14310e0656a045a7f691efab9fad9bf20225c1c0
7
- data.tar.gz: dec07e74c0db1b81f366652d075c419277d4805d80f216b972cb705ce2f6214d2bc8885f1ca827a47f33ff636e64adf3d61ca559cb50c636d1eee1a7aea94fb8
6
+ metadata.gz: 6911237facee346e2922ae792d8798f41a9a4090e86563979c77b4405bf6d40a20c6f81d2b4a7cc805e5e2d27af9a8699fea687b6f3c4c02d6669477073068b4
7
+ data.tar.gz: 7b68e177af76429287cc2c8d6c5f2322e87b1fe128d4aff76ead50ae154e2d802811c7101f3c09d19a4f2a7be99fabb10537f6b3926a8820ebec2df6b90bcff9
@@ -0,0 +1,37 @@
1
+ require 'backframe/image_cache/lib/asset'
2
+ require 'backframe/image_cache/lib/cache'
3
+ require 'backframe/image_cache/lib/conversions'
4
+
5
+ module Backframe
6
+
7
+ module ImageCache
8
+
9
+ class Base
10
+
11
+ def asset(filepath, conversions)
12
+ Backframe::ImageCache::Asset.new(filepath, conversions).process!
13
+ end
14
+
15
+ def cached(filepath, conversions)
16
+ Backframe::ImageCache::Cache.new(filepath, conversions).process!
17
+ end
18
+
19
+ def path(filepath, conversions)
20
+ string = Backframe::ImageCache::Conversions.new(conversions).to_s
21
+ filekey = filepath || 'default.jpg'
22
+ "/imagecache/#{string}/#{filekey}"
23
+ end
24
+
25
+ def url(filepath, conversions)
26
+ Rails.application.config.root_url + path(filepath, conversions)
27
+ end
28
+
29
+ def cdn_url(filepath, conversions)
30
+ Rails.application.config.cdn_url + path(filepath, conversions)
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,109 @@
1
+ require 'stringio'
2
+
3
+ module Backframe
4
+
5
+ module ImageCache
6
+
7
+ class Asset
8
+
9
+ def initialize(filepath, conversions)
10
+ @filepath = filepath
11
+ @conversions = conversions
12
+ end
13
+
14
+ def process!
15
+ normalized = Backframe::ImageCache::Conversions.new(conversions)
16
+ if filepath.present?
17
+ fullpath = fullpath(filepath)
18
+ content_type = content_type(filepath)
19
+ data = download(fullpath)
20
+ oriented = auto_orient(data)
21
+ command = expand(normalized)
22
+ converted = execute(oriented, command)
23
+ else
24
+ converted = default_asset
25
+ end
26
+ compressed = compress(converted)
27
+ OpenStruct.new({ :success => true, :data => compressed, :content_type => content_type })
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :filepath, :conversions
33
+
34
+ def fullpath(path)
35
+ File.exist?("#{Rails.root}/public/#{path}") ? "#{Rails.root}/public/#{path}" : "#{Rails.application.config.cdn_url}/#{path}"
36
+ end
37
+
38
+ def download(path)
39
+ open(path).read
40
+ end
41
+
42
+ def auto_orient(data)
43
+ execute(data, '-auto-orient')
44
+ end
45
+
46
+ def content_type(filepath)
47
+ ext = extension(filepath)
48
+ "image/#{ext}"
49
+ end
50
+
51
+ def extension(filepath)
52
+ filepath.match(/.*\.([a-z]*)$/)[1]
53
+ end
54
+
55
+ def expand(conversions)
56
+ command = []
57
+ conversions.to_a.each do |conversion|
58
+ if conversion.key?(:fit)
59
+ width = conversion[:fit][:width] * conversion[:density]
60
+ height = conversion[:fit][:height] * conversion[:density]
61
+ command << "-resize \"#{width}x#{height}^\" -gravity center -crop '#{width}x#{height}+0+0'"
62
+ elsif conversion.key?(:height)
63
+ height = conversion[:height] * conversion[:density]
64
+ command << "-resize \"x#{height}\""
65
+ elsif conversion.key?(:width)
66
+ width = conversion[:width] * conversion[:density]
67
+ command << "-resize \"#{width}\""
68
+ elsif conversion.key?(:crop)
69
+ command << "-crop '#{parts[1]}x#{parts[2]}+#{parts[3]}+#{parts[4]}'"
70
+ end
71
+ end
72
+ command.join(' ')
73
+ end
74
+
75
+ def default_asset
76
+ path = "#{Rails.root}/tmp/canvas.png"
77
+ command = "convert 10x10 canvas:#EEEEEE #{conversions} '#{path}'"
78
+ Rails.logger.debug(command)
79
+ output = `#{command}`
80
+ IO.read(path)
81
+ end
82
+
83
+ #################### UTILITIES ####################
84
+
85
+ def tmpfile(data)
86
+ filename = SecureRandom.hex(32).to_s.upcase[0,8]
87
+ tmpfile = Tempfile.new(filename, :encoding => 'ascii-8bit')
88
+ tmpfile.write(data)
89
+ tmpfile.close if tmpfile && !tmpfile.closed?
90
+ tmpfile
91
+ end
92
+
93
+ def execute(data, command)
94
+ tmpfile = tmpfile(data)
95
+ command = "convert '#{tmpfile.path}' #{command} '#{tmpfile.path}'"
96
+ Rails.logger.debug(command)
97
+ output = `#{command}`
98
+ IO.read(tmpfile.path)
99
+ end
100
+
101
+ def compress(data)
102
+ ActiveSupport::Gzip.compress(data)
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,67 @@
1
+ require 'stringio'
2
+
3
+ module Backframe
4
+
5
+ module ImageCache
6
+
7
+ class Cache
8
+
9
+ def initialize(filepath, conversions)
10
+ @filepath = filepath
11
+ @conversions = conversions
12
+ end
13
+
14
+ def process!
15
+ begin
16
+ test(filepath, conversions)
17
+ rescue
18
+ test(nil, conversions)
19
+ end
20
+ end
21
+
22
+ def test(filepath, conversions)
23
+ filekey = filepath || 'default.jpg'
24
+ normalized = Backframe::ImageCache::Conversions.new(conversions)
25
+ key = "imagecache/#{normalized.to_s}/#{filekey}"
26
+ if !redis.get(key)
27
+ asset = Backframe::ImageCache::Asset.new(filepath, normalized).process!
28
+ upload_to_s3(asset, key)
29
+ save_to_redis(key)
30
+ end
31
+ return OpenStruct.new(:success => true, :url => "#{Rails.application.config.cdn_url}/#{key}")
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :filepath, :conversions
37
+
38
+ def save_to_redis(key)
39
+ redis.set(key, true)
40
+ redis.expire(key, (7 * 24 * 60 * 60))
41
+ end
42
+
43
+ def upload_to_s3(asset, key)
44
+ headers = { :acl => 'public-read', :content_type => asset.content_type, :cache_control => 'max-age=315360000, no-transform, public', :content_encoding => 'gzip' }
45
+ bucket = s3.buckets[aws['bucket']]
46
+ bucket.objects.create(key, asset.data, headers)
47
+ end
48
+
49
+ def aws
50
+ @aws ||= YAML.load_file("#{Rails.root}/config/aws.yml")[Rails.env]
51
+ end
52
+
53
+ def s3
54
+ @s3 ||= AWS::S3.new(:access_key_id => aws['access_key_id'], :secret_access_key => aws['secret_access_key'])
55
+ end
56
+
57
+ def redis
58
+ return @redis if @redis.present?
59
+ config = YAML.load_file("#{Rails.root}/config/redis.yml")[Rails.env]
60
+ @redis = Redis.new(:host => config['host'], :port => config['port'], :db => 2)
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,132 @@
1
+ require 'stringio'
2
+
3
+ module Backframe
4
+
5
+ module ImageCache
6
+
7
+ class Conversions
8
+
9
+ def initialize(conversions)
10
+ @conversions = normalize_conversions(conversions)
11
+ end
12
+
13
+ def to_s
14
+ output = []
15
+ conversions.each do |conversion|
16
+ if conversion.key?(:fit)
17
+ output << "F#{conversion[:fit][:width]}X#{conversion[:fit][:height]}D#{conversion[:density]}"
18
+ elsif conversion.key?(:width)
19
+ output << "W#{conversion[:width]}D#{conversion[:density]}"
20
+ elsif conversion.key?(:height)
21
+ output << "H#{conversion[:height]}D#{conversion[:density]}"
22
+ elsif conversion.key?(:crop)
23
+ output << "C#{conversion[:crop][:width]}X#{conversion[:crop][:height]}X#{conversion[:crop][:x]}X#{conversion[:crop][:y]}D#{conversion[:density]}"
24
+ end
25
+ end
26
+ output.join("-")
27
+ end
28
+
29
+ def to_a
30
+ conversions
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :conversions
36
+
37
+ #################### CONVERSION HANDLING ####################
38
+
39
+ def normalize_conversions(conversions)
40
+ if conversions.is_a?(String)
41
+ return normalize_conversion_string(conversions)
42
+ elsif conversions.is_a?(Symbol)
43
+ return normalize_conversion_string(conversions.to_s)
44
+ elsif conversions.is_a?(Hash)
45
+ return normalize_conversion_array([conversions])
46
+ elsif conversions.is_a?(Array)
47
+ return normalize_conversion_array(conversions)
48
+ elsif conversions.is_a?(Backframe::ImageCache::Conversions)
49
+ return conversions.to_a
50
+ end
51
+ end
52
+
53
+ def normalize_conversion_string(string)
54
+ output = []
55
+ string.upcase.split("-").each do |part|
56
+ if part == 'PREVIEW'
57
+ output << { :width => 250, :density => 2 }
58
+ elsif part == 'TINY'
59
+ output << { :fit => { :width => 20, :height => 20 }, :density => 2 }
60
+ elsif part == 'SMALL'
61
+ output << { :fit => { :width => 40, :height => 40 }, :density => 2 }
62
+ elsif part == 'MEDIUM'
63
+ output << { :fit => { :width => 250, :height => 250 }, :density => 2 }
64
+ elsif matches = part.match(/^F(\d*)X(\d*)(D(\d*))?$/)
65
+ density = (matches[3]) ? matches[4] : 1
66
+ output << { :fit => { :width => matches[1].to_i, :height => matches[2].to_i }, :density => density.to_i }
67
+ elsif matches = part.match(/^W(\d*)(D(\d*))?$/)
68
+ density = (matches[2]) ? matches[3] : 1
69
+ output << { :width => matches[1].to_i, :density => density.to_i }
70
+ elsif matches = part.match(/^H(\d*)(D(\d*))?$/)
71
+ density = (matches[2]) ? matches[3] : 1
72
+ output << { :height => matches[1].to_i, :density => density.to_i }
73
+ elsif matches = part.match(/^C(\d*)X(\d*)X(\d*)X(\d*)(D(\d*))?$/)
74
+ density = (matches[4]) ? matches[5] : 1
75
+ output << { :crop => { :width => matches[1].to_i, :height => matches[2].to_i, :x => matches[3].to_i, :y => matches[4].to_i, :density => density.to_i } }
76
+ end
77
+ end
78
+ output
79
+ end
80
+
81
+ def normalize_conversion_array(array)
82
+ output = []
83
+ array.each do |conversion|
84
+ density = conversion[:density].to_i || 1
85
+ if conversion.key?(:preview)
86
+ output << { :width => 250, :density => density }
87
+ elsif conversion.key?(:tiny)
88
+ output << { :fit => { :width => 20, :height => 20 }, :density => density }
89
+ elsif conversion.key?(:small)
90
+ output << { :fit => { :width => 40, :height => 40 }, :density => density }
91
+ elsif conversion.key?(:medium)
92
+ output << { :fit => { :width => 250, :height => 250 }, :density => density }
93
+ elsif conversion.key?(:fit)
94
+ fit = (conversion[:fit].is_a?(String)) ? parse_geometry_string(conversion[:fit]) : parse_geometry_array(conversion[:fit])
95
+ output << { :fit => fit, :density => density }
96
+ elsif conversion.key?(:width)
97
+ output << { :width => conversion[:width].to_i, :density => density }
98
+ elsif conversion.key?(:height)
99
+ output << { :height => conversion[:height].to_i, :density => density }
100
+ elsif conversion.key?(:crop)
101
+ crop = (conversion[:crop].is_a?(String)) ? parse_geometry(conversion[:crop]) : parse_geometry_array(conversion[:crop])
102
+ output << { :crop => crop, :density => density }
103
+ end
104
+ end
105
+ output
106
+ end
107
+
108
+ def parse_geometry_string(string)
109
+ string = string.upcase
110
+ if matches = string.match(/^(\d*)X(\d*)$/)
111
+ { :width => matches[1].to_i, :height => matches[2].to_i }
112
+ elsif matches = string.match(/^(\d*)X(\d*)X(\d*)X(\d*)$/)
113
+ { :width => matches[1].to_i, :height => matches[2].to_i, :x => matches[3].to_i, :y => matches[4].to_i }
114
+ elsif matches = string.match(/^(\d*)X(\d*)\+(\d*)\+(\d*)$/)
115
+ { :width => matches[1].to_i, :height => matches[2].to_i, :x => matches[3].to_i, :y => matches[4].to_i }
116
+ end
117
+ end
118
+
119
+ def parse_geometry_array(array)
120
+ output = {}
121
+ output[:width] = array[:width].to_i if array.key?(:width)
122
+ output[:height] = array[:height].to_i if array.key?(:height)
123
+ output[:x] = array[:x].to_i if array.key?(:x)
124
+ output[:y] = array[:y].to_i if array.key?(:y)
125
+ output
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Backframe
4
- VERSION = '0.0.23'
4
+ VERSION = '0.0.24'
5
5
  end
data/lib/backframe.rb CHANGED
@@ -22,7 +22,7 @@ require 'backframe/models/activity'
22
22
  require 'backframe/models/activation'
23
23
  require 'backframe/models/reset'
24
24
  require 'backframe/serializers/activity_serializer'
25
- require 'backframe/image_cache'
25
+ require 'backframe/image_cache/image_cache'
26
26
 
27
27
  module Backframe
28
28
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backframe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.23
4
+ version: 0.0.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Kops
@@ -113,7 +113,10 @@ files:
113
113
  - lib/backframe/activerecord/default_values.rb
114
114
  - lib/backframe/activerecord/filter_sort.rb
115
115
  - lib/backframe/activerecord/migration.rb
116
- - lib/backframe/image_cache.rb
116
+ - lib/backframe/image_cache/image_cache.rb
117
+ - lib/backframe/image_cache/lib/asset.rb
118
+ - lib/backframe/image_cache/lib/cache.rb
119
+ - lib/backframe/image_cache/lib/conversions.rb
117
120
  - lib/backframe/mime.rb
118
121
  - lib/backframe/models/activation.rb
119
122
  - lib/backframe/models/activity.rb
@@ -1,129 +0,0 @@
1
- require 'stringio'
2
-
3
- module Backframe
4
-
5
- class ImageCache
6
-
7
- def initialize(filepath, conversions)
8
- @filepath = filepath
9
- @conversions = conversions
10
- end
11
-
12
- def process!
13
- normalized = normalize_conversions(conversions)
14
- fullpath = fullpath(filepath)
15
- content_type = content_type(filepath)
16
- data = download(fullpath)
17
- oriented = auto_orient(data)
18
- dimensions = dimensions(oriented)
19
- command = expand(normalized)
20
- converted = execute(oriented, command)
21
- OpenStruct.new({ :success => true, :data => converted, :content_type => content_type })
22
- end
23
-
24
- private
25
-
26
- attr_reader :filepath, :conversions
27
-
28
- #################### CONVERSION HANDLING ####################
29
-
30
- def normalize_conversions(string)
31
- output = []
32
- string.upcase.split("-").each do |part|
33
- if part == 'PREVIEW'
34
- output << { :width => 250, :density => 2 }
35
- elsif part == 'TINY'
36
- output << { :fit => { :width => 20, :height => 20 }, :density => 2 }
37
- elsif part == 'SMALL'
38
- output << { :fit => { :width => 40, :height => 40 }, :density => 2 }
39
- elsif part == 'MEDIUM'
40
- output << { :fit => { :width => 250, :height => 250 }, :density => 2 }
41
- elsif matches = part.match(/^F(\d*)X(\d*)(D(\d*))?$/)
42
- density = (matches[3]) ? matches[4].to_i : 1
43
- output << { :fit => { :width => matches[1].to_i, :height => matches[2].to_i }, :density => density }
44
- elsif matches = part.match(/^W(\d*)(D(\d*))?$/)
45
- density = (matches[2]) ? matches[3].to_i : 1
46
- output << { :width => matches[1].to_i, :density => density }
47
- elsif matches = part.match(/^H(\d*)(D(\d*))?$/)
48
- density = (matches[2]) ? matches[3].to_i : 1
49
- output << { :height => matches[1].to_i, :density => density }
50
- elsif matches = part.match(/^C(\d*)X(\d*)X(\d*)X(\d*)(D(\d*))?$/)
51
- density = (matches[4]) ? matches[5].to_i : 1
52
- output << { :crop => { :width => matches[1].to_i, :height => matches[2].to_i, :x => matches[3].to_i, :y => matches[4].to_i, :density => density } }
53
- end
54
- end
55
- output
56
- end
57
-
58
- #################### ASSET HANDLING ####################
59
-
60
- def fullpath(path)
61
- File.exist?("#{Rails.root}/public/#{path}") ? "#{Rails.root}/public/#{path}" : "#{Rails.application.config.cdn_url}/#{path}"
62
- end
63
-
64
- def download(path)
65
- open(path).read
66
- end
67
-
68
- def auto_orient(data)
69
- execute(data, '-auto-orient')
70
- end
71
-
72
- def content_type(filepath)
73
- ext = extension(filepath)
74
- "image/#{ext}"
75
- end
76
-
77
- def extension(filepath)
78
- filepath.match(/.*\.([a-z]*)$/)[1]
79
- end
80
-
81
- def dimensions(data)
82
- tmpfile = tmpfile(data)
83
- command = "identify -format '%wx%h,%[exif:orientation]' '#{tmpfile.path}'"
84
- output = `#{command}`
85
- matches = output.match(/^(\d*)x(\d*)/)
86
- { :width => matches[1], :height => matches[2] }
87
- end
88
-
89
- def expand(conversions)
90
- command = []
91
- conversions.each do |conversion|
92
- if conversion.key?(:fit)
93
- width = conversion[:fit][:width] * conversion[:density]
94
- height = conversion[:fit][:height] * conversion[:density].to_i
95
- command << "-resize \"#{width}x#{height}^\" -gravity center -crop '#{width}x#{height}+0+0'"
96
- elsif conversion.key?(:height)
97
- height = conversion[:height] * conversion[:density].to_i
98
- command << "-resize \"x#{height}\""
99
- elsif conversion.key?(:width)
100
- width = conversion[:width] * conversion[:density].to_i
101
- command << "-resize \"#{width}\""
102
- elsif conversion.key?(:crop)
103
- command << "-crop '#{parts[1]}x#{parts[2]}+#{parts[3]}+#{parts[4]}'"
104
- end
105
- end
106
- command.join(' ')
107
- end
108
-
109
- #################### UTILITIES ####################
110
-
111
- def tmpfile(data)
112
- filename = SecureRandom.hex(32).to_s.upcase[0,8]
113
- tmpfile = Tempfile.new(filename, :encoding => 'ascii-8bit')
114
- tmpfile.write(data)
115
- tmpfile.close if tmpfile && !tmpfile.closed?
116
- tmpfile
117
- end
118
-
119
- def execute(data, command)
120
- tmpfile = tmpfile(data)
121
- command = "convert '#{tmpfile.path}' #{command} '#{tmpfile.path}'"
122
- Rails.logger.debug(command)
123
- output = `#{command}`
124
- IO.read(tmpfile.path)
125
- end
126
-
127
- end
128
-
129
- end