backframe 0.0.23 → 0.0.24

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 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