shrinker 0.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/.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
|