ruby-thumbor 1.0.0 → 1.1.0

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.
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = ruby-thumbor {<img src="https://secure.travis-ci.org/heynemann/ruby-thumbor.png?branch=master" alt="Build Status" />}[http://travis-ci.org/heynemann/ruby-thumbor]
1
+ = ruby-thumbor {<img src="https://secure.travis-ci.org/heynemann/ruby-thumbor.png?branch=master" alt="Build Status" />}[http://travis-ci.org/heynemann/ruby-thumbor] {<img src="https://gemnasium.com/heynemann/ruby-thumbor.png" alt="Dependency Status" />}[https://gemnasium.com/heynemann/ruby-thumbor]
2
2
 
3
3
  * http://github.com/heynemann/ruby-thumbor
4
4
 
@@ -17,15 +17,32 @@ No dependencies required for regular usage.
17
17
 
18
18
  * thumbor (http://github.com/globocom/thumbor) for running ruby-thumbor tests.
19
19
 
20
+ == INSTALL:
21
+
22
+ gem install ruby-thumbor
23
+
24
+ gem 'ruby-thumbor'
25
+
26
+
20
27
  == USAGE:
21
28
 
22
- crypto = CryptoURL.new :key => 'my-security-key'
29
+ require 'ruby-thumbor'
30
+
31
+ crypto = Thumbor::CryptoURL.new :key => 'my-security-key'
23
32
 
24
33
  url = crypto.generate :width => 200, :height => 300, :image => 'my.server.com/path/to/image.jpg'
25
34
 
26
35
  # url will contain something like:
27
36
  # /2913921in321n3k2nj32hjhj3h22/my.server.com/path/to/image.jpg
28
37
 
38
+ or
39
+
40
+ require 'ruby-thumbor'
41
+
42
+ Thumbor.key = 'my-security-key'
43
+ image = Thumbor::Cascade.new('my.server.com/path/to/image.jpg')
44
+ image.width(300).height(200).watermark_filter('http://my-server.com/image.png', 30).generate
45
+
29
46
  Available arguments to the generate method:
30
47
 
31
48
  :meta => bool - flag that indicates that thumbor should return only meta-data on the operations it would
@@ -51,10 +68,6 @@ Available arguments to the generate method:
51
68
 
52
69
  If you need more info on what each option does, check thumbor's documentation at https://github.com/globocom/thumbor/wiki.
53
70
 
54
- == INSTALL:
55
-
56
- * sudo gem install ruby-thumbor
57
-
58
71
  == CONTRIBUTIONS:
59
72
 
60
73
  * Hugo Lopes (hugobr) - Fixes in the usage readme part of the docs.
data/lib/ruby-thumbor.rb CHANGED
@@ -1,146 +1,13 @@
1
- $:.unshift(File.dirname(__FILE__)) unless
2
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
-
4
- require 'openssl'
5
- require 'base64'
6
- require 'digest/md5'
7
- require 'cgi'
1
+ require 'thumbor/crypto_url'
2
+ require 'thumbor/cascade'
8
3
 
9
4
  module Thumbor
10
- VERSION = '1.0.0'
11
-
12
- class CryptoURL
13
- attr_accessor :key, :computed_key
14
-
15
- def initialize(key)
16
- @key = key
17
- @computed_key = (key * 16)[0..15]
18
- end
19
-
20
- def pad(s)
21
- s + ("{" * (16 - s.length % 16))
22
- end
23
-
24
- def calculate_width_and_height(url_parts, options)
25
- width = options[:width]
26
- height = options[:height]
27
-
28
- if width and options[:flip]
29
- width = width * -1
30
- end
31
- if height and options[:flop]
32
- height = height * -1
33
- end
34
-
35
- if width or height
36
- width = 0 if not width
37
- height = 0 if not height
38
- end
39
-
40
- has_width = width
41
- has_height = height
42
- if options[:flip] and not has_width and not has_height
43
- width = "-0"
44
- height = '0' if not has_height and not options[:flop]
45
- end
46
- if options[:flop] and not has_width and not has_height
47
- height = "-0"
48
- width = '0' if not has_width and not options[:flip]
49
- end
50
-
51
- if width or height
52
- width = width.to_s
53
- height = height.to_s
54
- url_parts.push(width << 'x' << height)
55
- end
56
- end
57
-
58
- def url_for(options, include_hash = true)
59
- if not options[:image]
60
- raise 'image is a required argument.'
61
- end
62
-
63
- url_parts = Array.new
64
-
65
- if options[:meta]
66
- url_parts.push('meta')
67
- end
68
-
69
- crop = options[:crop]
70
- if crop
71
- crop_left = crop[0]
72
- crop_top = crop[1]
73
- crop_right = crop[2]
74
- crop_bottom = crop[3]
75
-
76
- if crop_left > 0 or crop_top > 0 or crop_bottom > 0 or crop_right > 0
77
- url_parts.push(crop_left.to_s << 'x' << crop_top.to_s << ':' << crop_right.to_s << 'x' << crop_bottom.to_s)
78
- end
79
- end
80
-
81
- if options[:fit_in]
82
- url_parts.push('fit-in')
83
- end
84
-
85
- calculate_width_and_height(url_parts, options)
86
-
87
- if options[:halign] and options[:halign] != :center
88
- url_parts.push(options[:halign])
89
- end
90
-
91
- if options[:valign] and options[:valign] != :middle
92
- url_parts.push(options[:valign])
93
- end
94
-
95
- if options[:smart]
96
- url_parts.push('smart')
97
- end
98
-
99
- if options[:filters] && !options[:filters].empty?
100
- filter_parts = []
101
- options[:filters].each do |filter|
102
- filter_parts.push(filter)
103
- end
104
-
105
- url_parts.push("filters:#{ filter_parts.join(':') }")
106
- end
107
-
108
- if include_hash
109
- image_hash = Digest::MD5.hexdigest(options[:image])
110
- url_parts.push(image_hash)
111
- end
112
-
113
- return url_parts.join('/')
114
- end
115
-
116
- def url_safe_base64(str)
117
- Base64.encode64(str).gsub('+', '-').gsub('/', '_').gsub!(/[\n]/, '')
118
- end
119
-
120
- def generate_old(options)
121
- url = pad(url_for(options))
122
- cipher = OpenSSL::Cipher::Cipher.new('aes-128-ecb').encrypt
123
- cipher.key = @computed_key
124
- encrypted = cipher.update(url)
125
- based = url_safe_base64(encrypted)
126
-
127
- "/#{based}/#{options[:image]}"
128
- end
129
-
130
- def generate_new(options)
131
- url_options = url_for(options, false)
132
- url = "#{url_options}/#{options[:image]}"
133
-
134
- signature = OpenSSL::HMAC.digest('sha1', @key, url)
135
- signature = url_safe_base64(signature)
136
-
137
- "/#{signature}/#{url}"
138
- end
139
-
140
- def generate(options)
141
- return generate_old(options) if options[:old]
142
- generate_new(options)
143
- end
144
- end
5
+ def self.key=(key)
6
+ @@key = key
7
+ end
145
8
 
9
+ def self.key
10
+ @@key
11
+ end
146
12
  end
13
+
@@ -0,0 +1,70 @@
1
+ require 'forwardable'
2
+ require 'openssl'
3
+ require 'base64'
4
+ require 'digest/md5'
5
+ require 'cgi'
6
+
7
+ module Thumbor
8
+ class Cascade
9
+ attr_accessor :image, :old_crypto, :options, :filters
10
+
11
+ @available_options = [:meta, :crop, :width, :height, :flip,:flop, :halign, :valign, :smart, :fit_in, :old, :trim]
12
+
13
+ extend Forwardable
14
+
15
+ def_delegators :@old_crypto, :computed_key
16
+
17
+ @available_options.each do |opt|
18
+ define_method(opt) do |*args|
19
+ args = [true] if args.empty?
20
+ @options[opt] = args
21
+ self
22
+ end
23
+ end
24
+
25
+ def initialize(image=nil)
26
+ @image = image
27
+ @options = {}
28
+ @filters = []
29
+ @old_crypto = Thumbor::CryptoURL.new Thumbor.key
30
+ end
31
+
32
+ def url_for
33
+ @old_crypto.url_for prepare_options(@options).merge({:image => @image, :filters => @filters})
34
+ end
35
+
36
+ def generate
37
+ @old_crypto.generate prepare_options(@options).merge({:image => @image, :filters => @filters})
38
+ end
39
+
40
+ def method_missing(m, *args)
41
+ if /^(.+)_filter$/.match(m.to_s)
42
+ @filters << "#{$1}(#{escape_args(args).join(',')})"
43
+ self
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def escape_args(args)
52
+ args.map do |arg|
53
+ arg = CGI::escape(arg) if arg.is_a? String and arg.match(/^https?:\/\//)
54
+ arg
55
+ end
56
+ end
57
+
58
+ def prepare_options(options)
59
+ options.reduce({}) do |final_options, item|
60
+ value = if item[1].length == 1
61
+ item[1].first
62
+ else
63
+ item[1]
64
+ end
65
+ final_options[item[0]] = value
66
+ final_options
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,146 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require 'digest/md5'
4
+ require 'cgi'
5
+
6
+ module Thumbor
7
+ class CryptoURL
8
+ attr_accessor :computed_key
9
+
10
+ def initialize(key)
11
+ Thumbor.key = key
12
+ @computed_key = (Thumbor.key * 16)[0..15]
13
+ end
14
+
15
+ def pad(s)
16
+ s + ("{" * (16 - s.length % 16))
17
+ end
18
+
19
+ def calculate_width_and_height(url_parts, options)
20
+ width = options[:width]
21
+ height = options[:height]
22
+
23
+ if width and options[:flip]
24
+ width = width * -1
25
+ end
26
+ if height and options[:flop]
27
+ height = height * -1
28
+ end
29
+
30
+ if width or height
31
+ width = 0 if not width
32
+ height = 0 if not height
33
+ end
34
+
35
+ has_width = width
36
+ has_height = height
37
+ if options[:flip] and not has_width and not has_height
38
+ width = "-0"
39
+ height = '0' if not has_height and not options[:flop]
40
+ end
41
+ if options[:flop] and not has_width and not has_height
42
+ height = "-0"
43
+ width = '0' if not has_width and not options[:flip]
44
+ end
45
+
46
+ if width or height
47
+ width = width.to_s
48
+ height = height.to_s
49
+ url_parts.push(width << 'x' << height)
50
+ end
51
+ end
52
+
53
+ def url_for(options, include_hash = true)
54
+ if not options[:image]
55
+ raise 'image is a required argument.'
56
+ end
57
+
58
+ url_parts = Array.new
59
+
60
+ if options[:meta]
61
+ url_parts.push('meta')
62
+ end
63
+
64
+ crop = options[:crop]
65
+ if crop
66
+ crop_left = crop[0]
67
+ crop_top = crop[1]
68
+ crop_right = crop[2]
69
+ crop_bottom = crop[3]
70
+
71
+ if crop_left > 0 or crop_top > 0 or crop_bottom > 0 or crop_right > 0
72
+ url_parts.push(crop_left.to_s << 'x' << crop_top.to_s << ':' << crop_right.to_s << 'x' << crop_bottom.to_s)
73
+ end
74
+ end
75
+
76
+ if options[:fit_in]
77
+ url_parts.push('fit-in')
78
+ end
79
+
80
+ calculate_width_and_height(url_parts, options)
81
+
82
+ if options[:halign] and options[:halign] != :center
83
+ url_parts.push(options[:halign])
84
+ end
85
+
86
+ if options[:valign] and options[:valign] != :middle
87
+ url_parts.push(options[:valign])
88
+ end
89
+
90
+ if options[:smart]
91
+ url_parts.push('smart')
92
+ end
93
+
94
+ if options[:trim]
95
+ trim_options = ['trim']
96
+ trim_options << options[:trim] unless options[:trim] == true or options[:trim][0] == true
97
+ url_parts.push(trim_options.join(':'))
98
+ end
99
+
100
+ if options[:filters] && !options[:filters].empty?
101
+ filter_parts = []
102
+ options[:filters].each do |filter|
103
+ filter_parts.push(filter)
104
+ end
105
+
106
+ url_parts.push("filters:#{ filter_parts.join(':') }")
107
+ end
108
+
109
+ if include_hash
110
+ image_hash = Digest::MD5.hexdigest(options[:image])
111
+ url_parts.push(image_hash)
112
+ end
113
+
114
+ return url_parts.join('/')
115
+ end
116
+
117
+ def url_safe_base64(str)
118
+ Base64.encode64(str).gsub('+', '-').gsub('/', '_').gsub!(/[\n]/, '')
119
+ end
120
+
121
+ def generate_old(options)
122
+ url = pad(url_for(options))
123
+ cipher = OpenSSL::Cipher::Cipher.new('aes-128-ecb').encrypt
124
+ cipher.key = @computed_key
125
+ encrypted = cipher.update(url)
126
+ based = url_safe_base64(encrypted)
127
+
128
+ "/#{based}/#{options[:image]}"
129
+ end
130
+
131
+ def generate_new(options)
132
+ url_options = url_for(options, false)
133
+ url = "#{url_options}/#{options[:image]}"
134
+
135
+ signature = OpenSSL::HMAC.digest('sha1', Thumbor.key, url)
136
+ signature = url_safe_base64(signature)
137
+
138
+ "/#{signature}/#{url}"
139
+ end
140
+
141
+ def generate(options)
142
+ return generate_old(options) if options[:old]
143
+ generate_new(options)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,3 @@
1
+ module Thumbor
2
+ VERSION = '1.1.0'
3
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,15 @@
1
- $:.unshift(File.dirname(__FILE__) + '/../lib')
2
- require 'ruby-thumbor'
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ if /^1\.9/ === RUBY_VERSION
5
+ require 'simplecov'
6
+
7
+ SimpleCov.start do
8
+ add_filter '/spec/'
9
+ end
10
+ end
11
+
12
+ RSpec.configure do |c|
13
+ c.filter_run :focus => true
14
+ c.run_all_when_everything_filtered = true
15
+ end