shrinker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +83 -0
- data/Rakefile +1 -0
- data/lib/shrinker/backend/abstract.rb +25 -0
- data/lib/shrinker/backend/redis.rb +56 -0
- data/lib/shrinker/config.rb +83 -0
- data/lib/shrinker/easy_url.rb +36 -0
- data/lib/shrinker/extractor.rb +24 -0
- data/lib/shrinker/parser/abstract.rb +58 -0
- data/lib/shrinker/parser/mime.rb +44 -0
- data/lib/shrinker/parser/text.rb +21 -0
- data/lib/shrinker/parser/url.rb +41 -0
- data/lib/shrinker/serializer.rb +20 -0
- data/lib/shrinker/token.rb +23 -0
- data/lib/shrinker/version.rb +13 -0
- data/lib/shrinker.rb +48 -0
- data/shrinker.gemspec +20 -0
- data/spec/lib/backend/redis_spec.rb +77 -0
- data/spec/lib/config_spec.rb +66 -0
- data/spec/lib/easy_url_spec.rb +26 -0
- data/spec/lib/extractor_spec.rb +37 -0
- data/spec/lib/parser/mime_spec.rb +114 -0
- data/spec/lib/parser/text_spec.rb +110 -0
- data/spec/lib/parser/url_spec.rb +118 -0
- data/spec/lib/serializer_spec.rb +23 -0
- data/spec/lib/shrinker_spec.rb +11 -0
- data/spec/lib/token_spec.rb +24 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/fixtures/mime.txt +41 -0
- data/spec/support/fixtures/multipart_mime.txt +88 -0
- metadata +108 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3-p194@shrinker --install --create
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 jrichardlai
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Shrinker
|
2
|
+
|
3
|
+
Replace all occurence of a domain and create a shortened link using a token.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'shrinker'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install shrinker
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
Shrinker.configure do
|
24
|
+
backend 'Redis'
|
25
|
+
backend_options client: {port: 6388, host: '192.168.12.22'}
|
26
|
+
expanded_domain /(www.)?google.com/ # this also works with protocol
|
27
|
+
exclude_path /assets|images/
|
28
|
+
anchors\_only\_in_html true # With the mime only replace the links inside an anchor
|
29
|
+
shrinked_domain 'go.com'
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Not shrinking a link `www.google.com?shrinker=false&something=else` would be replaced by `www.google.com?something=else`.
|
34
|
+
|
35
|
+
Usage on a text:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
|
39
|
+
text = <<-EV
|
40
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
41
|
+
Nunc quis rutrum http://www.google.com?something=true&else=false dolor.
|
42
|
+
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
43
|
+
Curabitur ullamcorper nisl non dolor http://google.fr?something=true venenatis consequat.
|
44
|
+
Morbi odio libero, tincidunt quis tempus a, fringilla vitae augue.
|
45
|
+
http://google.com/somepath?something=true
|
46
|
+
Aenean placerat ullamcorper lorem vel feugiat.
|
47
|
+
EV
|
48
|
+
|
49
|
+
|
50
|
+
new_text = Shrinker::Parser::Text.replace(text, {:user_id => 123})
|
51
|
+
|
52
|
+
new_text # =>
|
53
|
+
# Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
54
|
+
# Nunc quis rutrum http://go.com/token1 dolor.
|
55
|
+
# Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
56
|
+
# Curabitur ullamcorper nisl non dolor http://google.fr?something=true venenatis consequat.
|
57
|
+
# Morbi odio libero, tincidunt quis tempus a, fringilla vitae augue.
|
58
|
+
# http://go.com/token2
|
59
|
+
# Aenean placerat ullamcorper lorem vel feugiat.
|
60
|
+
|
61
|
+
```
|
62
|
+
|
63
|
+
With a MIME:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
new_mime = Shrinker::Parser::Mime.replace(mime, {:user_id => 123})
|
67
|
+
```
|
68
|
+
|
69
|
+
Get back a real link:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
url = Shrinker::unshrink(token)
|
73
|
+
url.to_s # => 'http://google.com/something=true'
|
74
|
+
url.attributes['user_id'] # => 123
|
75
|
+
```
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
1. Fork it
|
80
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
81
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
82
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
83
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Shrinker
|
2
|
+
module Backend
|
3
|
+
class Abstract
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def store(raw_url, token, attributes = {})
|
11
|
+
raise "Shrinker::Backend::Abstract.store not implemented"
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch(token)
|
15
|
+
raise "Shrinker::Backend::Abstract.fetch not implemented"
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def ttl
|
21
|
+
options[:expires_in]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Shrinker
|
2
|
+
module Backend
|
3
|
+
class Redis < Abstract
|
4
|
+
require 'redis'
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
super
|
8
|
+
@client_options = options[:client] || {}
|
9
|
+
@namespace = options[:namespace] || '_shrinker'
|
10
|
+
end
|
11
|
+
|
12
|
+
def store(raw_url, token, attributes = {})
|
13
|
+
content = Shrinker::Serializer::to_json(raw_url, attributes)
|
14
|
+
redis_key = key(token)
|
15
|
+
if ttl
|
16
|
+
client.setex(redis_key, ttl, content)
|
17
|
+
else
|
18
|
+
client.set(redis_key, content)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def fetch(token)
|
23
|
+
content = client.get(key(token))
|
24
|
+
|
25
|
+
return {} unless content
|
26
|
+
Shrinker::Serializer::from_json(content)
|
27
|
+
end
|
28
|
+
|
29
|
+
def used_token?(token)
|
30
|
+
!!client.get(key(token))
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def client
|
36
|
+
@client ||= begin
|
37
|
+
::Redis.new(@client_options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(token)
|
42
|
+
client.del(key(token))
|
43
|
+
end
|
44
|
+
|
45
|
+
def clear!(pattern = nil)
|
46
|
+
client.keys("#{@namespace}::#{pattern || '*'}").each do |key|
|
47
|
+
client.del(key)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def key(token)
|
52
|
+
[@namespace, token].compact.join('::')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Shrinker
|
2
|
+
class Config
|
3
|
+
|
4
|
+
# provides a getter and a setter with the same method
|
5
|
+
# config_setting :dog
|
6
|
+
# --> dog 'Husky' :=> 'Husky' (setter)
|
7
|
+
# --> dog :=> 'Husky' (getter)
|
8
|
+
|
9
|
+
def self.settings
|
10
|
+
@settings ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.config_setting(name)
|
14
|
+
settings << name
|
15
|
+
|
16
|
+
class_eval <<-EV, __FILE__, __LINE__ + 1
|
17
|
+
attr_reader :#{name}
|
18
|
+
|
19
|
+
def #{name}(value = nil)
|
20
|
+
return @#{name} if value.nil?
|
21
|
+
@#{name} = value
|
22
|
+
value
|
23
|
+
end
|
24
|
+
EV
|
25
|
+
end
|
26
|
+
|
27
|
+
# the backend to be used to store the real url
|
28
|
+
config_setting :backend
|
29
|
+
|
30
|
+
# the options to pass to the backend
|
31
|
+
config_setting :backend_options
|
32
|
+
|
33
|
+
# domain to be shrinked can be a regex
|
34
|
+
config_setting :expanded_pattern
|
35
|
+
|
36
|
+
# regex for the url path to be excluded
|
37
|
+
config_setting :exclude_path
|
38
|
+
|
39
|
+
# regex to mactch/exclude the pattern when matching around patterns
|
40
|
+
config_setting :around_pattern
|
41
|
+
|
42
|
+
# domain to be used when shrinking the urls
|
43
|
+
config_setting :shrinked_pattern
|
44
|
+
|
45
|
+
# setting boolean to replace links only in anchor tags href inside html
|
46
|
+
config_setting :anchors_only_in_html
|
47
|
+
|
48
|
+
def ==(config)
|
49
|
+
self.class.settings.each { |setting| send(setting) == config.send(setting) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def reset!
|
53
|
+
self.instance_variables.each do |var|
|
54
|
+
self.instance_variable_set(var, nil)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def merge(config)
|
59
|
+
new_config = Config.new
|
60
|
+
new_config.merge!(config)
|
61
|
+
|
62
|
+
new_config
|
63
|
+
end
|
64
|
+
|
65
|
+
def merge!(config)
|
66
|
+
case config
|
67
|
+
when self.class
|
68
|
+
self.class.settings.each { |setting| send(setting, config.send(setting)) }
|
69
|
+
when Hash
|
70
|
+
config.each_pair do |setting, value|
|
71
|
+
send(setting, value)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def backend_instance
|
77
|
+
@backend_instance ||= begin
|
78
|
+
class_name = (backend || 'Abstract').to_s
|
79
|
+
::Shrinker::Backend.const_get(class_name).new(backend_options || {})
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Shrinker
|
2
|
+
class EasyUrl < Struct.new(:url, :attributes)
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
def =~(regexp)
|
6
|
+
to_s =~ regexp
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
parsed_uri.query = to_param
|
11
|
+
parsed_uri.query = nil if parsed_uri.query == ''
|
12
|
+
parsed_uri.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def params
|
16
|
+
@params ||= begin
|
17
|
+
hash = {}
|
18
|
+
CGI.parse(parsed_uri.query).each_pair do |key, value|
|
19
|
+
hash[key.to_sym] = value.length == 1 ? value.first : value
|
20
|
+
end if parsed_uri.query
|
21
|
+
hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def to_param
|
28
|
+
return params.to_param('') if params.respond_to?(:to_param)
|
29
|
+
params.collect { |key, value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}" }.join("&")
|
30
|
+
end
|
31
|
+
|
32
|
+
def parsed_uri
|
33
|
+
@parsed_uri ||= URI.parse(url)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Shrinker
|
2
|
+
class Extractor < Struct.new(:token, :config)
|
3
|
+
def self.unshrink(token, config = nil)
|
4
|
+
self.new(token, config).unshrink
|
5
|
+
end
|
6
|
+
|
7
|
+
def unshrink
|
8
|
+
stored_content = backend.fetch(token)
|
9
|
+
raise UrlNotFound.new("Cannot find the url with token: #{token}") if stored_content == {}
|
10
|
+
|
11
|
+
EasyUrl.new(stored_content['url'], stored_content['attributes'] || {})
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def backend
|
17
|
+
config.backend_instance
|
18
|
+
end
|
19
|
+
|
20
|
+
def config
|
21
|
+
super || Shrinker.config
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Shrinker
|
2
|
+
module Parser
|
3
|
+
class Abstract < Struct.new(:content, :attributes)
|
4
|
+
attr_reader :config
|
5
|
+
|
6
|
+
# Delegate replace to an instance
|
7
|
+
def self.replace(content, attributes = {}, config = nil)
|
8
|
+
self.new(content, attributes, config).replace
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(content = nil, attributes = nil, config = nil)
|
12
|
+
super(content, attributes)
|
13
|
+
merge_config(config)
|
14
|
+
end
|
15
|
+
|
16
|
+
def replace
|
17
|
+
raise "Shrinker::Parser::Abstract.replace not implemented"
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def url_regex
|
23
|
+
return base_url_regex if around_pattern.to_s == ''
|
24
|
+
|
25
|
+
Regexp.new around_pattern.to_s.gsub('$url', base_url_regex.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
def base_url_regex
|
29
|
+
/(#{expanded_pattern}[-A-Z0-9+&@#\/%?=~_|$!:,.;]*[-A-Z0-9+&@#\/%=~_|$])/i
|
30
|
+
end
|
31
|
+
|
32
|
+
def excluded_path_regex
|
33
|
+
config.exclude_path
|
34
|
+
end
|
35
|
+
|
36
|
+
def expanded_pattern
|
37
|
+
config.expanded_pattern
|
38
|
+
end
|
39
|
+
|
40
|
+
def around_pattern
|
41
|
+
config.around_pattern
|
42
|
+
end
|
43
|
+
|
44
|
+
def shrinked_pattern
|
45
|
+
config.shrinked_pattern
|
46
|
+
end
|
47
|
+
|
48
|
+
def backend
|
49
|
+
config.backend_instance
|
50
|
+
end
|
51
|
+
|
52
|
+
def merge_config(config)
|
53
|
+
@config ||= Shrinker.config.dup
|
54
|
+
@config.merge!(config)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Shrinker
|
2
|
+
module Parser
|
3
|
+
class Mime < Abstract
|
4
|
+
require 'mail'
|
5
|
+
|
6
|
+
def replace
|
7
|
+
parts = email.all_parts.empty? ? [email] : email.all_parts
|
8
|
+
|
9
|
+
parts.each do |part|
|
10
|
+
new_body = replace_part_body(part)
|
11
|
+
part.body = if part.content_transfer_encoding
|
12
|
+
Mail::Body.new(new_body).encoded(part.content_transfer_encoding)
|
13
|
+
else
|
14
|
+
new_body.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
email.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def email
|
21
|
+
@email ||= Mail.read_from_string(content)
|
22
|
+
end
|
23
|
+
|
24
|
+
def replace_part_body(part)
|
25
|
+
replace_config = config.dup
|
26
|
+
if part.mime_type == "text/html" && anchors_only_in_html?
|
27
|
+
replace_config.merge!({:around_pattern => anchor_tag_around_regex})
|
28
|
+
end
|
29
|
+
|
30
|
+
Text::replace(part.body.decoded, attributes, replace_config)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def anchor_tag_around_regex
|
36
|
+
/href="(?:https?:\/\/)?($url)"/
|
37
|
+
end
|
38
|
+
|
39
|
+
def anchors_only_in_html?
|
40
|
+
config.anchors_only_in_html == true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Shrinker
|
2
|
+
module Parser
|
3
|
+
class Text < Abstract
|
4
|
+
def replace
|
5
|
+
content.gsub(url_regex) do |url|
|
6
|
+
matched_url = $1
|
7
|
+
|
8
|
+
next if matched_url =~ excluded_path_regex
|
9
|
+
|
10
|
+
url.gsub!(matched_url, shrink_url(matched_url))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def shrink_url(url)
|
17
|
+
Url::replace(url, attributes, config)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Shrinker
|
2
|
+
module Parser
|
3
|
+
class Url < Abstract
|
4
|
+
def replace
|
5
|
+
return url.to_s if url.params.delete(:shrinker).to_s == 'false'
|
6
|
+
return url.to_s if url =~ excluded_path_regex
|
7
|
+
|
8
|
+
new_url = url.to_s
|
9
|
+
new_url = shrink if new_url =~ url_regex
|
10
|
+
|
11
|
+
new_url
|
12
|
+
end
|
13
|
+
|
14
|
+
def shrink
|
15
|
+
token = Token.fetch_unique_token(backend, prefix: "__#{attributes.to_json}__#{content}")
|
16
|
+
backend.store(content, token, attributes)
|
17
|
+
|
18
|
+
if shrinked_pattern.respond_to?(:call)
|
19
|
+
if match = url.to_s.match(expanded_pattern)
|
20
|
+
start = shrinked_pattern.call(match)
|
21
|
+
else
|
22
|
+
# just bail out
|
23
|
+
return url.to_s
|
24
|
+
end
|
25
|
+
else
|
26
|
+
start = shrinked_pattern.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
[start, token].join("/")
|
30
|
+
end
|
31
|
+
|
32
|
+
def url
|
33
|
+
@url ||= EasyUrl.new(content)
|
34
|
+
end
|
35
|
+
|
36
|
+
def url_regex
|
37
|
+
base_url_regex
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Shrinker
|
2
|
+
module Serializer
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def to_json(raw_url, attributes = {})
|
6
|
+
to_hash(raw_url, attributes).to_json
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_hash(raw_url, attributes = {})
|
10
|
+
{:url => raw_url, :attributes => (attributes || {})}
|
11
|
+
end
|
12
|
+
|
13
|
+
# symbolize keys from the json if with_indifferent_access exists
|
14
|
+
def from_json(content)
|
15
|
+
hash = JSON.parse(content)
|
16
|
+
hash = hash.with_indifferent_access if hash.respond_to?(:with_indifferent_access)
|
17
|
+
hash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Shrinker
|
2
|
+
module Token
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def fetch_unique_token(backend, options = {})
|
8
|
+
need_token = true
|
9
|
+
|
10
|
+
while need_token
|
11
|
+
token = generate(options)
|
12
|
+
need_token = backend.used_token?(token)
|
13
|
+
end
|
14
|
+
|
15
|
+
token
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate(options = {})
|
19
|
+
prefix = options[:prefix]
|
20
|
+
Digest::MD5.hexdigest("#{prefix}__#{rand(99999)}")[-12..-1]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/shrinker.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "shrinker/version"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Shrinker
|
5
|
+
autoload :Config, 'shrinker/config'
|
6
|
+
autoload :Serializer, 'shrinker/serializer'
|
7
|
+
autoload :Extractor, 'shrinker/extractor'
|
8
|
+
autoload :EasyUrl, 'shrinker/easy_url'
|
9
|
+
autoload :Token, 'shrinker/token'
|
10
|
+
|
11
|
+
module Backend
|
12
|
+
autoload :Abstract, 'shrinker/backend/abstract'
|
13
|
+
autoload :Redis, 'shrinker/backend/redis'
|
14
|
+
end
|
15
|
+
|
16
|
+
module Parser
|
17
|
+
autoload :Abstract, 'shrinker/parser/abstract'
|
18
|
+
autoload :Url, 'shrinker/parser/url'
|
19
|
+
autoload :Text, 'shrinker/parser/text'
|
20
|
+
autoload :Mime, 'shrinker/parser/mime'
|
21
|
+
end
|
22
|
+
|
23
|
+
class UrlNotFound < ArgumentError; end;
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def configure(&block)
|
27
|
+
if block_given?
|
28
|
+
configuration.instance_eval(&block)
|
29
|
+
end
|
30
|
+
configuration
|
31
|
+
end
|
32
|
+
alias_method :config, :configure
|
33
|
+
|
34
|
+
def unshrink(token)
|
35
|
+
Shrinker::Extractor::unshrink(token, config)
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def configuration
|
41
|
+
@configuration ||= ::Shrinker::Config.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def backend
|
45
|
+
config.backend_instance
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/shrinker.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'shrinker/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "shrinker"
|
8
|
+
gem.version = Shrinker::VERSION
|
9
|
+
gem.authors = ["jrichardlai"]
|
10
|
+
gem.email = ["jrichardlai@gmail.com"]
|
11
|
+
gem.description = %q{Find links in a text and shorten them}
|
12
|
+
gem.summary = %q{Find links in a text and shorten them}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.add_dependency('json')
|
20
|
+
end
|