ivey-longurl 0.1.7

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 ADDED
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ doc
3
+ pkg
4
+ rdoc
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009, Fabien Jakimowicz <fabien@jakimowicz.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,13 @@
1
+ bin/longurl
2
+ ChangeLog
3
+ lib/longurl/constants.rb
4
+ lib/longurl/direct.rb
5
+ lib/longurl/exceptions.rb
6
+ lib/longurl/service.rb
7
+ lib/longurl.rb
8
+ LICENSE
9
+ longurl.gemspec
10
+ Manifest
11
+ Rakefile
12
+ README
13
+ TODO
data/README.rdoc ADDED
@@ -0,0 +1,73 @@
1
+ = LongURL
2
+
3
+ == DESCRIPTION
4
+ LongURL expands short urls (tinyurl, is.gd, ...) to original ones, using on LongURL.org, internal resolution or direct resolution
5
+ First, expand will try to expand url using longurl.org service.
6
+ Then, it will try to direct follow redirections on the given url and returns final one.
7
+
8
+ == SYNOPSIS
9
+ === Options
10
+ * <tt>:cache</tt> : cache object to use, must implement [] and []= functions.
11
+
12
+ === Types
13
+ <tt>url</tt> is expected to be a String and returns a String with the url.
14
+
15
+ === Examples
16
+ # simple expands
17
+ LongURL.expand("http://tinyurl.com/1c2") # => "http://www.google.com"
18
+ LongURL.expand("http://tinyurl.com/blnhsg") # => "http://www.google.com/search?q=number+of+horns+on+a+unicorn&ie=UTF-8"
19
+ LongURL.expand("http://is.gd/iUKg") # => "http://fabien.jakimowicz.com"
20
+
21
+ # not expandable urls, without any http call
22
+ LongURL.expand("http://www.linuxfr.org") # => "http://www.linuxfr.org"
23
+
24
+ # Use MemCache
25
+ LongURL.expand("http://is.gd/iUKg", :cache => MemCache.new("localhost:11211", :namespace => "LongURL"))
26
+ # => "http://fabien.jakimowicz.com"
27
+
28
+ # Expander class
29
+ expander = LongURL::Expander.new
30
+ expander.expand("http://tinyurl.com/1c2") # => "http://www.google.com"
31
+ # not expandable urls, direct resolution only
32
+ expander.direct_resolution("http://www.linuxfr.org") # => "http://www.linuxfr.org/pub"
33
+ # not expandable urls, calling longurl.org only
34
+ expander.expand_with_service_only("http://www.linuxfr.org") # => "http://www.linuxfr.org/pub"
35
+ # ... with MemCache
36
+ expander = LongURL::Expander.new(:cache => MemCache.new("localhost:11211", :namespace => "LongURL"))
37
+ expander.expand("http://tinyurl.com/1c2") # => "http://www.google.com"
38
+
39
+ === Exceptions
40
+ * LongURL::InvalidURL : will occurs if given url is nil, empty or invalid
41
+ * LongURL::NetworkError : a network (timeout, host could be reached, ...) error occurs
42
+ * LongURL::UnknownError : an unknown error occurs
43
+
44
+ == REQUIREMENTS
45
+
46
+ * json gem
47
+
48
+ == INSTALL
49
+ gem install longurl
50
+
51
+ == LICENSE
52
+
53
+ (The MIT License)
54
+
55
+ Copyright (c) 2009, Fabien Jakimowicz <fabien@jakimowicz.com>
56
+
57
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
58
+ this software and associated documentation files (the "Software"), to deal in
59
+ the Software without restriction, including without limitation the rights to
60
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
61
+ of the Software, and to permit persons to whom the Software is furnished to do
62
+ so, subject to the following conditions:
63
+
64
+ The above copyright notice and this permission notice shall be included in all
65
+ copies or substantial portions of the Software.
66
+
67
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
73
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,70 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "ivey-longurl"
7
+ s.summary = %q{LongURL expands shorten urls (tinyurl, is.gd, ...)}
8
+ s.homepage = "http://github.com/ivey/longurl"
9
+ s.description = %q{LongURL expands short urls (tinyurl, is.gd, ...) to original ones, using on LongURL.org, internal resolution or direct resolution}
10
+ s.authors = ["Fabien Jakimowicz", "Michael D. Ivey"]
11
+ s.email = "ivey@gweezlebur.com"
12
+ s.rubyforge_project = 'longurl'
13
+
14
+ s.add_dependency 'json'
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
18
+ end
19
+
20
+ desc "build rdoc using hanna theme"
21
+ task :rdoc do
22
+ `rm -rf rdoc && rdoc --op=rdoc --title=LongURL --inline-source --format=darkfish LICENSE README* lib/**/*.rb`
23
+ end
24
+
25
+ require 'rake/testtask'
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib' << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ begin
33
+ require 'rcov/rcovtask'
34
+ Rcov::RcovTask.new do |t|
35
+ t.libs << 'test'
36
+ t.test_files = FileList['test/**/*_test.rb']
37
+ t.verbose = true
38
+ end
39
+ rescue LoadError
40
+ puts "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
41
+ end
42
+
43
+ # Rubyforge publishing
44
+ begin
45
+ require 'rake/contrib/sshpublisher'
46
+ namespace :rubyforge do
47
+
48
+ desc "Release gem and RDoc documentation to RubyForge"
49
+ task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
50
+
51
+ namespace :release do
52
+ desc "Publish RDoc to RubyForge."
53
+ task :docs => [:rdoc] do
54
+ config = YAML.load(
55
+ File.read(File.expand_path('~/.rubyforge/user-config.yml'))
56
+ )
57
+
58
+ host = "#{config['username']}@rubyforge.org"
59
+ remote_dir = "/var/www/gforge-projects/longurl/"
60
+ local_dir = 'rdoc'
61
+
62
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
63
+ end
64
+ end
65
+ end
66
+ rescue LoadError
67
+ puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
68
+ end
69
+
70
+ task :default => :test
data/TODO ADDED
@@ -0,0 +1,6 @@
1
+ * add a cache to avoid multiple calls for same url
2
+ * cache could be : hash, memcache, ...
3
+ * handle http errors
4
+ * handle json parsing errors
5
+ * handle ServiceUnknown exception
6
+ * possible infinite loop in Direct::follow_redirections
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 7
3
+ :major: 0
4
+ :minor: 1
data/bin/longurl ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "longurl"
5
+
6
+ def usage
7
+ puts "Usage: #$0 <url>"
8
+ exit(-1)
9
+ end
10
+
11
+ if ARGV.size < 1
12
+ usage
13
+ end
14
+
15
+ puts LongURL.expand(ARGV[0])
data/lib/longurl.rb ADDED
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2009 by Fabien Jakimowicz (fabien@jakimowicz.com)
2
+ #
3
+ # Please see the LICENSE file for licensing.
4
+
5
+ require 'longurl/constants'
6
+ require 'longurl/exceptions'
7
+ require 'longurl/url'
8
+ require 'longurl/service'
9
+ require 'longurl/direct'
10
+ require 'longurl/expander'
11
+ require 'longurl/expand'
@@ -0,0 +1,9 @@
1
+ require "uri"
2
+
3
+ module LongURL
4
+ ShortURLMatchRegexp = /http:\/\/[\/\-_.a-z0-9]+/im
5
+
6
+ # Urls for longurl
7
+ EndPoint = URI.parse("http://api.longurl.org/v2/expand")
8
+ ServiceEndPoint = URI.parse("http://api.longurl.org/v2/services")
9
+ end
@@ -0,0 +1,26 @@
1
+ require "net/http"
2
+ require "longurl/url"
3
+ require "longurl/exceptions"
4
+
5
+ module LongURL
6
+ module Direct
7
+ # Will follow redirections given url <tt>orig</tt>.
8
+ # === Exceptions
9
+ # * LongURL::NetworkError in case of a network error (timeout, socket error, ...)
10
+ # * LongURL::InvalidURL in case of a bad url (nil, empty, not http scheme ...)
11
+ # * LongURL::TooManyRedirections if there are too many redirection for destination
12
+ def self.follow_redirections(orig, limit = 5)
13
+ raise LongURL::TooManyRedirections if limit == 0
14
+ uri = LongURL::URL.check(orig)
15
+ Net::HTTP.start(uri.host, uri.port) do |http|
16
+ answer = http.get(uri.path.empty? ? '/' : uri.path)
17
+ dest = answer['Location']
18
+ (dest && dest[0, 7] == 'http://' && follow_redirections(dest, limit - 1)) || orig
19
+ end
20
+ rescue Timeout::Error, Errno::ENETUNREACH, Errno::ETIMEDOUT, SocketError
21
+ raise LongURL::NetworkError
22
+ rescue
23
+ raise LongURL::UnknownError
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ module LongURL
2
+ # Raised by LongURL::Service class if longurl.org service returns unsupported service error.
3
+ class UnsupportedService < StandardError
4
+ end
5
+
6
+ # Raised by LongURL::Service class if longurl.org service returns a not supported answer.
7
+ class UnknownError < StandardError
8
+ end
9
+
10
+ # Raised by LongURL::Service if supplied url is invalid (nil, empty, ...)
11
+ class InvalidURL < StandardError
12
+ end
13
+
14
+ # Raised if a network error occurs : timeout, unreachable network, ...
15
+ class NetworkError < StandardError
16
+ end
17
+
18
+ # Raised if there are too many redirection in a direct resolution.
19
+ class TooManyRedirections < StandardError
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ require 'longurl/expander'
2
+
3
+ module LongURL
4
+
5
+ class << self
6
+
7
+ # Expand given <tt>:url</tt> to a longest one.
8
+ # First, expand will try to expand url using longurl.org service.
9
+ # Then, it will try to direct follow redirections on the given url and returns final one.
10
+ # === Options
11
+ # * <tt>:cache</tt> : cache object to use, must implement [] and []= functions.
12
+ # * <tt>:first_only</tt> : if true, users all-redirects option to the API to fetch only the first redirect
13
+ # instead of following all of them. Useful to identify the original URL that was shortened, not the
14
+ # destination.
15
+ # === Types
16
+ # <tt>url</tt> is expected to be a String and returns a String with the url.
17
+ # === Examples
18
+ # # simple expands
19
+ # LongURL.expand("http://tinyurl.com/1c2") # => "http://www.google.com"
20
+ # LongURL.expand("http://tinyurl.com/blnhsg") # => "http://www.google.com/search?q=number+of+horns+on+a+unicorn&ie=UTF-8"
21
+ # LongURL.expand("http://is.gd/iUKg") # => "http://fabien.jakimowicz.com"
22
+ #
23
+ # # not expandable urls
24
+ # LongURL.expand("http://www.linuxfr.org") # => "http://www.linuxfr.org"
25
+ #
26
+ # === Exceptions
27
+ # * LongURL::InvalidURL : will occurs if given url is nil, empty or invalid
28
+ # * LongURL::NetworkError : a network (timeout, host could be reached, ...) error occurs
29
+ # * LongURL::UnknownError : an unknown error occurs
30
+ def expand(url, options = {})
31
+ @@expander ||= Expander.new(
32
+ :cache => options[:cache],
33
+ :first_only => options[:first_only]
34
+ )
35
+ @@expander.expand(url)
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,95 @@
1
+ require "longurl/exceptions"
2
+ require "longurl/service"
3
+ require "longurl/direct"
4
+
5
+ module LongURL
6
+
7
+ # == URL Expander class.
8
+ # Will use Service and Direct classes to expand an url.
9
+ # Each call to external services is cached to save time and cache object is customizable.
10
+ # You can for example use MemCache for the cache. it will allow different instances of Expander and Service to share the same cache.
11
+ # === Examples
12
+ # # Simple usage
13
+ # e = LongURL::Expander.new
14
+ # e.expand("http://tinyurl.com/1c2") # => "http://www.google.com"
15
+ # e.expand("http://tinyurl.com/blnhsg") # => "http://www.google.com/search?q=number+of+horns+on+a+unicorn&ie=UTF-8"
16
+ # e.expand("http://is.gd/iUKg") # => "http://fabien.jakimowicz.com"
17
+ #
18
+ # # not expandable urls
19
+ # e.expand("http://www.linuxfr.org") # => "http://www.linuxfr.org"
20
+ #
21
+ # # not expandable urls, calling longurl.org only
22
+ # e.expand("http://www.linuxfr.org", :direct_resolution => true) # => "http://www.linuxfr.org/pub"
23
+ #
24
+ # # not expandable urls, direct resolution only
25
+ # e.direct_resolution("http://www.linuxfr.org") # => "http://www.linuxfr.org/pub"
26
+ #
27
+ # # MemCache as cache
28
+ # e = LongURL::Expander.new(:cache => MemCache.new("localhost:11211", :namespace => "LongURL"))
29
+ # e.expand("http://is.gd/iUKg") # => "http://fabien.jakimowicz.com"
30
+ # === Exceptions
31
+ # * LongURL::InvalidURL : will occurs if given url is nil, empty or invalid
32
+ # * LongURL::NetworkError : a network (timeout, host could be reached, ...) error occurs
33
+ # * LongURL::UnknownError : an unknown error occurs
34
+ class Expander
35
+
36
+ attr_accessor :first_only
37
+
38
+ # Initialize a new Expander.
39
+ # === Options
40
+ # * <tt>:cache</tt>: define a cache which Expander can use.
41
+ # It must implements [] and []= methods. It can be disabled using false.
42
+ # * <tt>:first_only</tt> : if true, users all-redirects option to the API to fetch only the first redirect
43
+ # instead of following all of them. Useful to identify the original URL that was shortened, not the
44
+ # destination.
45
+ def initialize(options = {})
46
+ # OPTIMIZE : This code is a complete duplicate of cache handling in service.
47
+ if options[:cache].nil?
48
+ @@cache = Hash.new
49
+ elsif options[:cache] == false
50
+ @@cache = nil
51
+ else
52
+ @@cache = options[:cache]
53
+ end
54
+ @@service = Service.new(:cache => @@cache)
55
+ @first_only = options[:first_only]
56
+ end
57
+
58
+ # Expand given url using LongURL::Service class first and then try a direct_resolution,
59
+ # unless :direct_resolution is set to false in options hash.
60
+ def expand(url, options={ })
61
+ @@service.query_supported_service_only url, :first_only => first_only
62
+ rescue UnsupportedService
63
+ options[:direct_resolution] == false ? raise(UnsupportedService) : direct_resolution(url)
64
+ end
65
+
66
+ # Try to directly resolve url using LongURL::Direct to get final redirection.
67
+ # This call is cached.
68
+ def direct_resolution(url)
69
+ # OPTIMIZE : this code is almost identical as the one in service for handling service retrieval.
70
+ if @@cache
71
+ @@cache[url] ||= Direct.follow_redirections(url)
72
+ else
73
+ Direct.follow_redirections(url)
74
+ end
75
+ end
76
+
77
+ # Expand all url in the given string, if an error occurs while expanding url, then the original url is used.
78
+ # <tt>options</tt> accepts same options as expand, see expand for more details.
79
+ def expand_each_in(text, options = {})
80
+ text.gsub(ShortURLMatchRegexp) do |shorturl|
81
+ begin
82
+ expand shorturl
83
+ rescue InvalidURL,
84
+ NetworkError,
85
+ TooManyRedirections,
86
+ UnknownError,
87
+ UnsupportedService,
88
+ JSON::ParserError
89
+ shorturl
90
+ end
91
+ end
92
+ end # expand_each_in
93
+
94
+ end # Expander
95
+ end # LongURL
@@ -0,0 +1,121 @@
1
+ require "net/http"
2
+ require "cgi"
3
+ require "uri"
4
+ require "rubygems"
5
+ require "json"
6
+ require "longurl/constants"
7
+ require "longurl/exceptions"
8
+ require "longurl/url"
9
+
10
+ module LongURL
11
+
12
+ class Service
13
+
14
+ def initialize(params = {})
15
+ if params[:cache].nil?
16
+ @@cache = Hash.new
17
+ elsif params[:cache] == false
18
+ @@cache = nil
19
+ else
20
+ @@cache = params[:cache]
21
+ end
22
+ @@supported_services = cached_or_fetch_supported_services
23
+ end
24
+
25
+ def query_supported_service_only(url, options={ })
26
+ check url
27
+ raise LongURL::UnsupportedService unless service_supported?(url)
28
+ (@@cache && cached_query(url, options)) || query(url, options)
29
+ end
30
+
31
+ def cached_query(url, options={ })
32
+ @@cache[url] ||= query(url, options)
33
+ end
34
+
35
+ def query(url, options={ })
36
+ escaped_url = check_and_escape(url)
37
+ api_url = "#{EndPoint.path}?format=json&url=#{escaped_url}"
38
+ if options[:first_only]
39
+ api_url += "&all-redirects=1"
40
+ end
41
+ Net::HTTP.start(EndPoint.host, EndPoint.port) do |http|
42
+ handle_response http.get(api_url)
43
+ end
44
+ rescue Timeout::Error, Errno::ENETUNREACH, Errno::ETIMEDOUT, SocketError
45
+ raise LongURL::NetworkError
46
+ end
47
+
48
+ # Check among supported services by longurl.org if given <tt>url</tt> is supported.
49
+ # Returns true if supported, false otherwise.
50
+ def service_supported?(url)
51
+ @@supported_services.include? URI.parse(url).host.downcase
52
+ end
53
+
54
+ protected
55
+
56
+ # Returns a list of supported services.
57
+ # Use cache to get the list or fetch it if cache is empty.
58
+ def cached_or_fetch_supported_services
59
+ if @@cache
60
+ @@cache['supported_services'] ||= fetch_supported_services
61
+ else
62
+ fetch_supported_services
63
+ end
64
+ end
65
+
66
+ # Check given <tt>url</tt> using LongURL::URL.check
67
+ def check(url)
68
+ LongURL::URL.check url
69
+ end
70
+
71
+ # Check given url and escape it for http query argument passing.
72
+ def check_and_escape(url)
73
+ check url
74
+ CGI.escape url
75
+ end
76
+
77
+ # Fetch supported services from longurl.org api.
78
+ # Returns supported services in an Array.
79
+ # Raises LongURL::NetworkError in case of a network error (timeout, ...)
80
+ def fetch_supported_services
81
+ Net::HTTP.start(ServiceEndPoint.host, ServiceEndPoint.port) do |http|
82
+ response = http.get("#{ServiceEndPoint.path}?format=json")
83
+ parsed = JSON.parse(response.body)
84
+ parsed.values.collect { |x| x["domain"] }.flatten
85
+ end
86
+ rescue Timeout::Error, Errno::ENETUNREACH, Errno::ETIMEDOUT, SocketError
87
+ raise LongURL::NetworkError
88
+ rescue
89
+ raise LongURL::UnknownError
90
+ end
91
+
92
+ def handle_response(response)
93
+ parsed = JSON.parse(response.body)
94
+ parsed = parsed.first if parsed.is_a?(Array)
95
+ if parsed['all-redirects']
96
+ parsed['all-redirects'].first
97
+ elsif parsed['long-url']
98
+ parsed['long-url']
99
+ elsif parsed['message'] # Error
100
+ raise exception_regarding_message(parsed['message'])
101
+ else
102
+ raise LongURL::UnknownError
103
+ end
104
+ end
105
+
106
+ def exception_class_regarding_message(message)
107
+ case message
108
+ when 'Unsupported service.'
109
+ LongURL::UnsupportedService
110
+ when 'Connection timeout'
111
+ LongURL::NetworkError
112
+ when 'Could not expand URL. Please check that you have submitted a valid URL.'
113
+ LongURL::InvalidURL
114
+ else
115
+ LongURL::UnknownError
116
+ end
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,18 @@
1
+ require 'uri'
2
+ require "longurl/exceptions"
3
+
4
+ module LongURL
5
+ module URL
6
+ # Check given <tt>url</tt>
7
+ # Raises LongURL::InvalidURL if <tt>url</tt> is invalid.
8
+ # Returns a parsed http uri object on success.
9
+ def self.check(url)
10
+ raise LongURL::InvalidURL if url.nil? or url.empty?
11
+ result = URI.parse(url)
12
+ raise LongURL::InvalidURL unless result.is_a?(URI::HTTP)
13
+ result
14
+ rescue URI::InvalidURIError
15
+ raise LongURL::InvalidURL
16
+ end
17
+ end
18
+ end
data/longurl.gemspec ADDED
@@ -0,0 +1,75 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{longurl}
5
+ s.version = "0.1.6"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Fabien Jakimowicz"]
9
+ s.date = %q{2009-07-20}
10
+ s.default_executable = %q{longurl}
11
+ s.description = %q{LongURL expands short urls (tinyurl, is.gd, ...) to original ones, using on LongURL.org, internal resolution or direct resolution}
12
+ s.email = %q{fabien@jakimowicz.com}
13
+ s.executables = ["longurl"]
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "LICENSE",
21
+ "Manifest",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "TODO",
25
+ "VERSION.yml",
26
+ "bin/longurl",
27
+ "lib/longurl.rb",
28
+ "lib/longurl/constants.rb",
29
+ "lib/longurl/direct.rb",
30
+ "lib/longurl/exceptions.rb",
31
+ "lib/longurl/expand.rb",
32
+ "lib/longurl/expander.rb",
33
+ "lib/longurl/service.rb",
34
+ "lib/longurl/url.rb",
35
+ "longurl.gemspec",
36
+ "test/cache_mock.rb",
37
+ "test/constants.rb",
38
+ "test/expander_test.rb",
39
+ "test/service/no_cache_service_test.rb",
40
+ "test/service/service_cache_test.rb",
41
+ "test/service/service_test.rb",
42
+ "test/service/supported_services_test.rb",
43
+ "test/url_test.rb"
44
+ ]
45
+ s.has_rdoc = true
46
+ s.homepage = %q{http://longurl.rubyforge.org}
47
+ s.rdoc_options = ["--charset=UTF-8"]
48
+ s.require_paths = ["lib"]
49
+ s.rubyforge_project = %q{longurl}
50
+ s.rubygems_version = %q{1.3.1}
51
+ s.summary = %q{LongURL expands shorten urls (tinyurl, is.gd, ...)}
52
+ s.test_files = [
53
+ "test/cache_mock.rb",
54
+ "test/constants.rb",
55
+ "test/expander_test.rb",
56
+ "test/service/no_cache_service_test.rb",
57
+ "test/service/service_cache_test.rb",
58
+ "test/service/service_test.rb",
59
+ "test/service/supported_services_test.rb",
60
+ "test/url_test.rb"
61
+ ]
62
+
63
+ if s.respond_to? :specification_version then
64
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
+ s.specification_version = 2
66
+
67
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
68
+ s.add_runtime_dependency(%q<json>, [">= 0"])
69
+ else
70
+ s.add_dependency(%q<json>, [">= 0"])
71
+ end
72
+ else
73
+ s.add_dependency(%q<json>, [">= 0"])
74
+ end
75
+ end
@@ -0,0 +1,20 @@
1
+ class CacheMock
2
+
3
+ attr_accessor :keys_stored, :keys_asked, :storage
4
+
5
+ def initialize(params = {})
6
+ @storage = {}
7
+ @keys_stored = []
8
+ @keys_asked = []
9
+ end
10
+
11
+ def [](key)
12
+ @keys_asked << key
13
+ @storage[key]
14
+ end
15
+
16
+ def []=(key, value)
17
+ @keys_stored << key
18
+ @storage[key] = value
19
+ end
20
+ end
data/test/constants.rb ADDED
@@ -0,0 +1,15 @@
1
+ ShortToLong = {
2
+ :tiny_url => {
3
+ "http://tinyurl.com/cnuw9a" => "http://fabien.jakimowicz.com",
4
+ "http://tinyurl.com/blnhsg" => "http://www.google.com/search?q=number+of+horns+on+a+unicorn&ie=UTF-8"
5
+ },
6
+ :is_gd => {
7
+ "http://is.gd/iUKg" => "http://fabien.jakimowicz.com",
8
+ "http://is.gd/iYCo" => "http://www.google.com/search?q=number+of+horns+on+a+unicorn&ie=UTF-8"
9
+ },
10
+ :friendfeed => {
11
+ "http://ff.im/-31OFh" => "http://en.wikipedia.org/wiki/Product_requirements_document",
12
+ "http://ff.im/-31MWm" => "http://www.infrasystems.com/how-to-write-an-mrd.html"
13
+ }
14
+ }
15
+ MultipleRedirectURL = "http://bit.ly/9NiEKB"
@@ -0,0 +1,64 @@
1
+ $test_lib_dir = File.join(File.dirname(__FILE__), "..", "lib")
2
+ $:.unshift($test_lib_dir)
3
+
4
+ require "test/unit"
5
+ require "longurl/expander"
6
+
7
+ class TextExpander < Test::Unit::TestCase
8
+ def setup
9
+ @expander = LongURL::Expander.new
10
+ end
11
+
12
+ def test_expand_each_in_should_expand_friendfeed_urls
13
+ assert_equal "Product requirements document - Wikipedia, http://en.wikipedia.org/wiki/Product_requirements_document the free encyclopedia",
14
+ @expander.expand_each_in("Product requirements document - Wikipedia, http://ff.im/-31OFh the free encyclopedia")
15
+ end
16
+
17
+ def test_expand_each_in_should_not_change_strings_with_no_urls
18
+ assert_equal "i'm not to be changed !!!", @expander.expand_each_in("i'm not to be changed !!!")
19
+ end
20
+
21
+ def test_expand_each_in_should_be_able_to_expand_multiple_urls
22
+ assert_equal "Those websites are great: http://www.flickr.com/photos/jakimowicz & http://www.google.com/profiles/fabien.jakimowicz",
23
+ @expander.expand_each_in("Those websites are great: http://tinyurl.com/r9cm9p & http://is.gd/Bnxy")
24
+ end
25
+
26
+ def test_expand_each_in_should_not_replace_unexpandable_urls_if_direct_resolution_is_false
27
+ assert_equal "Those websites are great: http://www.flickr.com/photos/jakimowicz & http://www.google.com/profiles/fabien.jakimowicz",
28
+ @expander.expand_each_in( "Those websites are great: http://www.flickr.com/photos/jakimowicz & http://is.gd/Bnxy",
29
+ :direct_resolution => false )
30
+ end
31
+
32
+ def test_expand_should_raise_UnsupportedService_if_service_is_not_supported_and_direct_resolution_disabled
33
+ assert_raise(LongURL::UnsupportedService) do
34
+ @expander.expand("http://www.google.com", :direct_resolution => false)
35
+ end
36
+ end
37
+
38
+ def test_expand_should_use_direct_resolution_by_default
39
+ assert_nothing_raised do
40
+ assert_equal "http://fabien.jakimowicz.com", @expander.expand("http://fabien.jakimowicz.com")
41
+ end
42
+ end
43
+
44
+ def test_expand_should_expand_supported_services
45
+ assert_equal "http://www.flickr.com/photos/jakimowicz", @expander.expand('http://tinyurl.com/r9cm9p')
46
+ end
47
+
48
+ def test_expand_should_handle_direct_resolution_if_asked
49
+ assert_equal "http://fabien.jakimowicz.com", @expander.expand("http://fabien.jakimowicz.com", :direct_resolution => true)
50
+ end
51
+
52
+ def test_expand_with_should_continue_to_resolve_urls_even_if_direct_resolution_is_true
53
+ assert_equal "http://www.flickr.com/photos/jakimowicz", @expander.expand('http://tinyurl.com/r9cm9p', :direct_resolution => true)
54
+ end
55
+
56
+ def test_first_only_defaults_to_false
57
+ assert !@expander.first_only
58
+ end
59
+
60
+ def test_expand_with_first_only_should_resolve_to_first_not_final
61
+ expander = LongURL::Expander.new :first_only => true
62
+ assert_equal "http://gmail.com", expander.expand(MultipleRedirectURL)
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ $test_lib_dir = File.join(File.dirname(__FILE__), "..", "lib")
2
+ $:.unshift($test_lib_dir)
3
+
4
+ require "test/unit"
5
+ require "constants"
6
+ require "longurl/exceptions"
7
+ require "longurl/service"
8
+
9
+ class TestNoCacheService < Test::Unit::TestCase
10
+ # OPTIMIZE : all these tests are a plain copy from service_test.rb, we can make something better.
11
+
12
+ def setup
13
+ @service = LongURL::Service.new(:cache => false)
14
+ end
15
+
16
+ def test_query_should_raise_invalid_url_if_url_is_nil
17
+ assert_raise(LongURL::InvalidURL) { @service.query(nil) }
18
+ end
19
+
20
+ def test_query_should_raise_invalid_url_if_url_is_empty
21
+ assert_raise(LongURL::InvalidURL) { @service.query('') }
22
+ end
23
+
24
+ def test_query_should_returns_given_url_if_not_shorten_url
25
+ assert_equal "http://www.google.com", @service.query("http://www.google.com")
26
+ end
27
+
28
+ def test_query_should_returns_expanded_url_for_supported_services
29
+ ShortToLong.each_value {|service| service.each {|short, long| assert_equal long, @service.query(short)}}
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ $test_lib_dir = File.join(File.dirname(__FILE__), "..", "lib")
2
+ $:.unshift($test_lib_dir)
3
+
4
+ require "test/unit"
5
+ require "cache_mock"
6
+ require "constants"
7
+ require "longurl/exceptions"
8
+ require "longurl/service"
9
+
10
+ class TestServiceCache < Test::Unit::TestCase
11
+
12
+ def setup
13
+ @cache = CacheMock.new
14
+ @service = LongURL::Service.new(:cache => @cache)
15
+ end
16
+
17
+ def test_query_should_use_cache_before_external_fetch
18
+ url = ShortToLong[:is_gd].keys.first
19
+ @service.query_supported_service_only(url)
20
+ assert_equal ['supported_services', url], @cache.keys_asked
21
+ @service.query_supported_service_only(url)
22
+ assert_equal ['supported_services', url, url], @cache.keys_asked
23
+ end
24
+
25
+ def test_query_should_cache_results_from_supported_services
26
+ ShortToLong.each_value do |service|
27
+ service.each do |short, long|
28
+ @service.query_supported_service_only(short)
29
+ assert @cache.keys_stored.include?(short)
30
+ assert_equal long, @cache[short]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ $test_lib_dir = File.join(File.dirname(__FILE__), "..", "lib")
2
+ $:.unshift($test_lib_dir)
3
+
4
+ require "test/unit"
5
+ require "longurl/exceptions"
6
+ require "longurl/service"
7
+
8
+ class TestService < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @service = LongURL::Service.new
12
+ end
13
+
14
+ def test_query_should_raise_invalid_url_if_url_is_nil
15
+ assert_raise(LongURL::InvalidURL) { @service.query(nil) }
16
+ end
17
+
18
+ def test_query_should_raise_invalid_url_if_url_is_empty
19
+ assert_raise(LongURL::InvalidURL) { @service.query('') }
20
+ end
21
+
22
+ def test_query_should_returns_given_url_if_not_shorten_url
23
+ assert_equal "http://www.google.com", @service.query("http://www.google.com")
24
+ end
25
+
26
+ def test_query_should_returns_expanded_url_for_supported_services
27
+ ShortToLong.each_value {|service| service.each {|short, long| assert_equal long, @service.query(short)}}
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ $test_lib_dir = File.join(File.dirname(__FILE__), "..", "lib")
2
+ $:.unshift($test_lib_dir)
3
+
4
+ require "test/unit"
5
+ require "cache_mock"
6
+ require "constants"
7
+ require "longurl/exceptions"
8
+ require "longurl/service"
9
+
10
+ class TestSupportedServices < Test::Unit::TestCase
11
+
12
+ def setup
13
+ @cache = CacheMock.new
14
+ end
15
+
16
+ def test_service_should_check_if_available_services_are_in_cache
17
+ assert_equal [], @cache.keys_asked
18
+ @service = LongURL::Service.new(:cache => @cache)
19
+ assert_equal ['supported_services'], @cache.keys_asked
20
+ end
21
+
22
+ def test_service_should_store_available_services_in_cache
23
+ assert_equal [], @cache.keys_stored
24
+ @service = LongURL::Service.new(:cache => @cache)
25
+ assert_equal ['supported_services'], @cache.keys_stored
26
+ end
27
+
28
+ def test_supported_services_stored_in_cache_should_be_a_flat_array_of_strings
29
+ @service = LongURL::Service.new(:cache => @cache)
30
+ assert_kind_of Array, @cache['supported_services']
31
+ assert @cache['supported_services'].all? {|object| object.is_a?(String)}
32
+ end
33
+
34
+ def test_service_should_use_supported_services_stored_in_cache_if_available
35
+ @cache['supported_services'] = ['bleh.com', 'bli.com']
36
+ @service = LongURL::Service.new(:cache => @cache)
37
+ assert_equal ['supported_services'], @cache.keys_asked
38
+ assert_equal ['supported_services'], @cache.keys_stored
39
+ assert_raise(LongURL::UnsupportedService) { @service.query_supported_service_only(ShortToLong[:is_gd].keys.first) }
40
+ end
41
+ end
data/test/url_test.rb ADDED
@@ -0,0 +1,44 @@
1
+ $test_lib_dir = File.join(File.dirname(__FILE__), "..", "lib")
2
+ $:.unshift($test_lib_dir)
3
+
4
+ require "test/unit"
5
+ require "uri"
6
+ require "longurl/exceptions"
7
+ require "longurl/url"
8
+
9
+ class TestURL < Test::Unit::TestCase
10
+
11
+ BadURLs = ["http:",
12
+ "http://",
13
+ "http://hoth.entp..."]
14
+
15
+ NotHTTPURLs = ["bleh",
16
+ "bleh://",
17
+ "bleh://asfd.com",
18
+ "ftp://asdf.com",
19
+ "google.com",
20
+ "asdf@toto.com",
21
+ "httpd://asdf.com"]
22
+
23
+ GoodURLs = ["http://www.google.com", "https://rubyonrails.org"]
24
+
25
+ def test_check_should_raise_invalid_url_if_url_is_nil
26
+ assert_raise(LongURL::InvalidURL) { LongURL::URL.check nil }
27
+ end
28
+
29
+ def test_check_should_raise_invalid_url_if_url_is_empty
30
+ assert_raise(LongURL::InvalidURL) { LongURL::URL.check "" }
31
+ end
32
+
33
+ def test_check_should_raise_invalid_url_if_url_is_invalid
34
+ BadURLs.each {|bad_url| assert_raise(LongURL::InvalidURL) { LongURL::URL.check bad_url } }
35
+ end
36
+
37
+ def test_check_should_raise_invalid_url_if_url_is_not_http
38
+ NotHTTPURLs.each {|bad_url| assert_raise(LongURL::InvalidURL) { LongURL::URL.check bad_url } }
39
+ end
40
+
41
+ def test_check_should_returns_parsed_url_on_success
42
+ GoodURLs.each {|good_url| assert_equal URI.parse(good_url), LongURL::URL.check(good_url)}
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ivey-longurl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.7
5
+ platform: ruby
6
+ authors:
7
+ - Fabien Jakimowicz
8
+ - Michael D. Ivey
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2010-05-12 00:00:00 -04:00
14
+ default_executable: longurl
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: json
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ version:
26
+ description: LongURL expands short urls (tinyurl, is.gd, ...) to original ones, using on LongURL.org, internal resolution or direct resolution
27
+ email: ivey@gweezlebur.com
28
+ executables:
29
+ - longurl
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - LICENSE
34
+ - README.rdoc
35
+ - TODO
36
+ files:
37
+ - .gitignore
38
+ - LICENSE
39
+ - Manifest
40
+ - README.rdoc
41
+ - Rakefile
42
+ - TODO
43
+ - VERSION.yml
44
+ - bin/longurl
45
+ - lib/longurl.rb
46
+ - lib/longurl/constants.rb
47
+ - lib/longurl/direct.rb
48
+ - lib/longurl/exceptions.rb
49
+ - lib/longurl/expand.rb
50
+ - lib/longurl/expander.rb
51
+ - lib/longurl/service.rb
52
+ - lib/longurl/url.rb
53
+ - longurl.gemspec
54
+ - test/cache_mock.rb
55
+ - test/constants.rb
56
+ - test/expander_test.rb
57
+ - test/service/no_cache_service_test.rb
58
+ - test/service/service_cache_test.rb
59
+ - test/service/service_test.rb
60
+ - test/service/supported_services_test.rb
61
+ - test/url_test.rb
62
+ has_rdoc: true
63
+ homepage: http://github.com/ivey/longurl
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --charset=UTF-8
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ requirements: []
84
+
85
+ rubyforge_project: longurl
86
+ rubygems_version: 1.3.5
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: LongURL expands shorten urls (tinyurl, is.gd, ...)
90
+ test_files:
91
+ - test/cache_mock.rb
92
+ - test/constants.rb
93
+ - test/expander_test.rb
94
+ - test/service/no_cache_service_test.rb
95
+ - test/service/service_cache_test.rb
96
+ - test/service/service_test.rb
97
+ - test/service/supported_services_test.rb
98
+ - test/url_test.rb