firefly 0.4.0.1 → 0.4.1
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/HISTORY +14 -0
- data/VERSION +1 -1
- data/firefly.gemspec +2 -3
- data/lib/firefly.rb +1 -0
- data/lib/firefly/server.rb +18 -10
- data/lib/firefly/url.rb +18 -1
- data/spec/firefly/api_spec.rb +41 -6
- data/spec/firefly/url_spec.rb +31 -1
- data/views/index.haml +26 -0
- metadata +2 -4
- data/API.md +0 -86
data/HISTORY
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
= 0.4.1 / 2010-04-30
|
2
|
+
|
3
|
+
* 2010-04-30 - Normalize URLs before shortening them. This prevents false duplicates. [ariejan]
|
4
|
+
|
5
|
+
* 2010-04-30 - Validate URLs to be valid HTTP or HTTPS, don't accept others. [ariejan]
|
6
|
+
|
7
|
+
* 2010-04-30 - Don't ask for the API key after shortening a URL with the bookmarklet. [ariejan]
|
8
|
+
|
9
|
+
* 2010-04-15 - Show the highlighted URL separately. Closes #7. [ariejan]
|
10
|
+
|
11
|
+
= 0.4.0.1 / 2010-04-14
|
12
|
+
|
13
|
+
* 2010-04-14 - Correct file permission of `public/images/twitter.png`. [ariejan]
|
14
|
+
|
1
15
|
= 0.4.0 / 2010-04-14
|
2
16
|
|
3
17
|
* 2010-04-14 - Added button to quickly tweet a URL. Closes #3 [ariejan]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.1
|
data/firefly.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{firefly}
|
8
|
-
s.version = "0.4.
|
8
|
+
s.version = "0.4.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ariejan de Vroom"]
|
12
|
-
s.date = %q{2010-04-
|
12
|
+
s.date = %q{2010-04-30}
|
13
13
|
s.description = %q{FireFly is a simple URL shortner for personal use. It's powered by Sinatra and can be run with any Rack-capable web server.}
|
14
14
|
s.email = %q{ariejan@ariejan.net}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -18,7 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
]
|
19
19
|
s.files = [
|
20
20
|
".gitignore",
|
21
|
-
"API.md",
|
22
21
|
"HISTORY",
|
23
22
|
"LICENSE",
|
24
23
|
"README.md",
|
data/lib/firefly.rb
CHANGED
data/lib/firefly/server.rb
CHANGED
@@ -53,8 +53,13 @@ module Firefly
|
|
53
53
|
|
54
54
|
if !url.nil? && url != ""
|
55
55
|
ff_url = Firefly::Url.shorten(url)
|
56
|
-
|
57
|
-
|
56
|
+
if !ff_url.nil?
|
57
|
+
code = ff_url.code
|
58
|
+
result = "http://#{config[:hostname]}/#{code}"
|
59
|
+
else
|
60
|
+
code = nil
|
61
|
+
result = "ERROR: The URL you posted is invalid."
|
62
|
+
end
|
58
63
|
end
|
59
64
|
|
60
65
|
return code, result
|
@@ -69,6 +74,12 @@ module Firefly
|
|
69
74
|
def tweet(url)
|
70
75
|
config[:tweet].gsub('%short_url%', url)
|
71
76
|
end
|
77
|
+
|
78
|
+
def store_api_key(key)
|
79
|
+
if key == config[:api_key]
|
80
|
+
set_api_cookie(config[:api_key])
|
81
|
+
end
|
82
|
+
end
|
72
83
|
end
|
73
84
|
|
74
85
|
before do
|
@@ -79,19 +90,14 @@ module Firefly
|
|
79
90
|
end
|
80
91
|
|
81
92
|
get '/' do
|
82
|
-
@highlight
|
93
|
+
@highlight = Firefly::Url.first(:code => params[:highlight])
|
94
|
+
@error = params[:highlight] == "error"
|
83
95
|
@urls = Firefly::Url.all(:limit => config[:recent_urls], :order => [ :created_at.desc ])
|
84
96
|
haml :index
|
85
97
|
end
|
86
98
|
|
87
99
|
post '/api/set' do
|
88
|
-
|
89
|
-
puts "MATCH!"
|
90
|
-
set_api_cookie(config[:api_key])
|
91
|
-
else
|
92
|
-
puts "NOT MATCH"
|
93
|
-
end
|
94
|
-
|
100
|
+
store_api_key(params[:api_key])
|
95
101
|
redirect '/'
|
96
102
|
end
|
97
103
|
|
@@ -106,6 +112,8 @@ module Firefly
|
|
106
112
|
@result ||= "Invalid URL specified."
|
107
113
|
|
108
114
|
if params[:visual]
|
115
|
+
store_api_key(params[:api_key])
|
116
|
+
@code ||= "error"
|
109
117
|
redirect "/?highlight=#{@code}"
|
110
118
|
else
|
111
119
|
@result
|
data/lib/firefly/url.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module Firefly
|
2
2
|
class Url
|
3
3
|
include DataMapper::Resource
|
4
|
-
|
4
|
+
|
5
|
+
VALID_URL_REGEX = /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/ix
|
6
|
+
|
5
7
|
property :id, Serial
|
6
8
|
property :url, String, :index => true, :length => 255
|
7
9
|
property :code, String, :index => true, :length => 16
|
@@ -15,9 +17,24 @@ module Firefly
|
|
15
17
|
|
16
18
|
# Shorten a long_url and return a new FireFly::Url
|
17
19
|
def self.shorten(long_url)
|
20
|
+
return nil unless valid_url?(long_url)
|
21
|
+
|
22
|
+
long_url = normalize_url(long_url)
|
23
|
+
|
18
24
|
the_url = Firefly::Url.first(:url => long_url) || Firefly::Url.create(:url => long_url)
|
19
25
|
the_url.update(:code => Firefly::Base62.encode(the_url.id.to_i)) if the_url.code.nil?
|
20
26
|
the_url
|
21
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
# Normalize the URL
|
31
|
+
def self.normalize_url(url)
|
32
|
+
URI.parse(url).normalize.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
# Validates the URL to be a valid http or https one.
|
36
|
+
def self.valid_url?(url)
|
37
|
+
url.match(Firefly::Url::VALID_URL_REGEX)
|
38
|
+
end
|
22
39
|
end
|
23
40
|
end
|
data/spec/firefly/api_spec.rb
CHANGED
@@ -10,18 +10,53 @@ describe "API" do
|
|
10
10
|
[:post, :get].each do |verb|
|
11
11
|
describe "adding a URL by #{verb.to_s.upcase}" do
|
12
12
|
it "should be okay adding a new URL" do
|
13
|
-
self.send verb, '/api/add', :url => 'http://example.org', :api_key => 'test'
|
13
|
+
self.send verb, '/api/add', :url => 'http://example.org/', :api_key => 'test'
|
14
14
|
last_response.should be_ok
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should return the shortened URL" do
|
18
|
-
self.send verb, '/api/add', :url => 'http://example.org', :api_key => 'test'
|
19
|
-
|
20
|
-
url = Firefly::Url.first(:url => "http://example.org")
|
18
|
+
self.send verb, '/api/add', :url => 'http://example.org/', :api_key => 'test'
|
19
|
+
url = Firefly::Url.first(:url => "http://example.org/")
|
21
20
|
|
22
21
|
last_response.body.should eql("http://test.host/#{url.code}")
|
23
22
|
end
|
24
23
|
|
24
|
+
it "should show an error when shortening an invalid URL" do
|
25
|
+
self.send verb, '/api/add', :url => 'ftp://example.org', :api_key => 'test'
|
26
|
+
|
27
|
+
last_response.body.should match("The URL you posted is invalid")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should show an error when shortening an invalid URL in visual mode" do
|
31
|
+
self.send verb, '/api/add', :url => 'ftp://example.org', :api_key => 'test', :visual => "1"
|
32
|
+
follow_redirect!
|
33
|
+
|
34
|
+
last_response.body.should match("The URL you posted is invalid")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should redirect to the highlighted URL when visual is enabled" do
|
38
|
+
self.send verb, '/api/add', :url => 'http://example.org/', :api_key => 'test', :visual => "1"
|
39
|
+
follow_redirect!
|
40
|
+
|
41
|
+
last_request.path.should eql("/")
|
42
|
+
last_request.should be_get
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should store the API key in the session with visual enabled" do
|
46
|
+
self.send verb, '/api/add', :url => 'http://example.org/', :api_key => 'test', :visual => "1"
|
47
|
+
follow_redirect!
|
48
|
+
|
49
|
+
last_response.body.should_not match(/API Key/)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should highlight the shortened URL" do
|
53
|
+
self.send verb, '/api/add', :url => 'http://example.org/', :api_key => 'test', :visual => "1"
|
54
|
+
url = Firefly::Url.first(:url => "http://example.org/")
|
55
|
+
follow_redirect!
|
56
|
+
|
57
|
+
last_request.query_string.should match(/highlight=#{url.code}/)
|
58
|
+
end
|
59
|
+
|
25
60
|
it "should return a 401 on a wrong API key" do
|
26
61
|
self.send verb, '/api/add', :url => 'http://example.org', :api_key => 'false'
|
27
62
|
last_response.status.should eql(401)
|
@@ -41,8 +76,8 @@ describe "API" do
|
|
41
76
|
}.should_not change(Firefly::Url, :count).by(1)
|
42
77
|
end
|
43
78
|
end
|
44
|
-
end
|
45
|
-
|
79
|
+
end
|
80
|
+
|
46
81
|
describe "getting information" do
|
47
82
|
before(:each) do
|
48
83
|
@created_at = Time.now
|
data/spec/firefly/url_spec.rb
CHANGED
@@ -3,7 +3,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
3
3
|
describe "Url" do
|
4
4
|
|
5
5
|
describe "shortening" do
|
6
|
-
|
7
6
|
it "should generate a code after create" do
|
8
7
|
url = Firefly::Url.shorten("http://example.com/")
|
9
8
|
Firefly::Url.first(:url => "http://example.com/").code.should_not be_nil
|
@@ -26,6 +25,37 @@ describe "Url" do
|
|
26
25
|
Firefly::Url.shorten("http://example.com/")
|
27
26
|
}.should_not change(Firefly::Url, :count)
|
28
27
|
end
|
28
|
+
|
29
|
+
it "should normalize urls correctly" do
|
30
|
+
# Note the trailing '/'
|
31
|
+
Firefly::Url.shorten("http://example.com/")
|
32
|
+
lambda {
|
33
|
+
Firefly::Url.shorten("http://example.com")
|
34
|
+
}.should_not change(Firefly::Url, :count)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "long url validation" do
|
39
|
+
[ "http://ariejan.net",
|
40
|
+
"https://ariejan.net",
|
41
|
+
"http://ariejan.net/page/1",
|
42
|
+
"http://ariejan.net/page/1?q=x&p=123",
|
43
|
+
"http://ariejan.net:8080/"
|
44
|
+
].each do |url|
|
45
|
+
it "should accept #{url}" do
|
46
|
+
Firefly::Url.shorten(url).should_not be_nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
[ "ftp://ariejan.net",
|
51
|
+
"irc://freenode.org/rails",
|
52
|
+
"skype:adevroom",
|
53
|
+
"ariejan.net",
|
54
|
+
].each do |url|
|
55
|
+
it "should not accept #{url}" do
|
56
|
+
Firefly::Url.shorten(url).should be_nil
|
57
|
+
end
|
58
|
+
end
|
29
59
|
end
|
30
60
|
|
31
61
|
describe "clicking" do
|
data/views/index.haml
CHANGED
@@ -17,7 +17,33 @@
|
|
17
17
|
%input.big_url{ :type => 'text', :name => 'url', :id => 'url', :size => 50, :autocomplete => "off", :spellcheck => 'false' }
|
18
18
|
%p
|
19
19
|
%input.big_url{ :type => 'submit', :name => 'submit', :id => 'submit', :value => "Make it short!" }
|
20
|
+
|
21
|
+
- if @error
|
22
|
+
%h2 Whoops!
|
23
|
+
|
24
|
+
%p
|
25
|
+
The URL you posted is invalid. Please post a valid HTTP url, including the protocol (http://) prefix.
|
20
26
|
|
27
|
+
- if @highlight
|
28
|
+
%h2 Your short URL
|
29
|
+
|
30
|
+
%table
|
31
|
+
%tr
|
32
|
+
%td.label Short URL
|
33
|
+
%td.label Full URL
|
34
|
+
%td.label Clicks
|
35
|
+
%td.label Shortened at
|
36
|
+
%td.label
|
37
|
+
%tr.highlighted
|
38
|
+
%td.value
|
39
|
+
%input{ :type => "text", :value => short_url(@highlight), :class => 'short_url', :size => 21 }
|
40
|
+
%td.value.fill <a href="#{@highlight.url}">#{@highlight.url}</a>
|
41
|
+
%td.value.center= @highlight.clicks
|
42
|
+
%td.value= @highlight.created_at.strftime("%Y-%m-%d %H:%M")
|
43
|
+
%td.value
|
44
|
+
%a{ :href => "http://twitter.com/home?status=#{tweet(short_url(@highlight))}" }
|
45
|
+
%img.twitter{ :src => "/images/twitter.png", :width => "16", :height => "16" }
|
46
|
+
|
21
47
|
%h2 Recently shortened
|
22
48
|
|
23
49
|
%table
|
metadata
CHANGED
@@ -5,9 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 4
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
version: 0.4.
|
9
|
+
version: 0.4.1
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Ariejan de Vroom
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-30 00:00:00 +02:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -127,7 +126,6 @@ extra_rdoc_files:
|
|
127
126
|
- README.md
|
128
127
|
files:
|
129
128
|
- .gitignore
|
130
|
-
- API.md
|
131
129
|
- HISTORY
|
132
130
|
- LICENSE
|
133
131
|
- README.md
|
data/API.md
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
# FireFly API
|
2
|
-
|
3
|
-
FireFly exposes its data via an Application Programming Interface (API), so developers can interact in a programmatic way with the FireFly website. This document is the official reference for that functionality. The current API version is 1.0
|
4
|
-
|
5
|
-
## Request / Response
|
6
|
-
|
7
|
-
FireFly responds in JSON.
|
8
|
-
|
9
|
-
A normal response may look like this:
|
10
|
-
|
11
|
-
{
|
12
|
-
"status_code": 200,
|
13
|
-
"data": {
|
14
|
-
"url": "http://example.com/cmeH01",
|
15
|
-
"hash": "cmeH01",
|
16
|
-
"long_url": "http://ariejan.net/",
|
17
|
-
"new_hash": 0
|
18
|
-
}
|
19
|
-
}
|
20
|
-
|
21
|
-
**Status codes**
|
22
|
-
|
23
|
-
FireFly sends back a `status_code`:
|
24
|
-
|
25
|
-
* 200 - OK
|
26
|
-
* 401 - Permission denied - wrong API key
|
27
|
-
* 404 - Hash or short URL not found (only for `expand` and `clicks`)
|
28
|
-
* 500 - Something bad happened.
|
29
|
-
|
30
|
-
## /v1/shorten
|
31
|
-
|
32
|
-
Shorten a long url to a short one.
|
33
|
-
|
34
|
-
GET /v1/shorten
|
35
|
-
|
36
|
-
**Parameters**
|
37
|
-
|
38
|
-
* `api_key` - Your API key, used for authentication
|
39
|
-
* `long_url` - The long URL. E.g. 'http://ariejan.net/'
|
40
|
-
|
41
|
-
**Output**
|
42
|
-
|
43
|
-
* `new_hash` - Is 1 if it's the first time the `long_url` is shortened, 0 otherwise.
|
44
|
-
* `url` - The short URL
|
45
|
-
* `hash` - The FireFly hash, this is a unique value
|
46
|
-
* `long_url` - Echos back the `long_url` that was shortened
|
47
|
-
|
48
|
-
## /v1/expand
|
49
|
-
|
50
|
-
Expand a short URL or hash to the long url.
|
51
|
-
|
52
|
-
GET /v1/expand
|
53
|
-
|
54
|
-
**Parameters**
|
55
|
-
|
56
|
-
* `short_url` - A valid short URL
|
57
|
-
* `hash` - A valid hash
|
58
|
-
* `api_key` - Your API key
|
59
|
-
|
60
|
-
_Note that either `short_url` or `hash` must be specified_
|
61
|
-
|
62
|
-
**Output**
|
63
|
-
|
64
|
-
* `short_url` - The short URL
|
65
|
-
* `hash` - The FireFly hash
|
66
|
-
* `long_url` - The original long URL
|
67
|
-
|
68
|
-
## /v1/clicks
|
69
|
-
|
70
|
-
Request the number of clicks of a specific short URL or hash.
|
71
|
-
|
72
|
-
GET /v1/clicks
|
73
|
-
|
74
|
-
**Parameters**
|
75
|
-
|
76
|
-
* `api_key` - Your API key
|
77
|
-
* `short_url` - A short URL
|
78
|
-
* `hash` - A hash
|
79
|
-
|
80
|
-
_Note that either `short_url` or `hash` must be specified_
|
81
|
-
|
82
|
-
**Output**
|
83
|
-
|
84
|
-
* `short_url` - The short URL
|
85
|
-
* `hash` - The FireFly hash
|
86
|
-
* `clicks` - The total number of clicks on this link
|