firefly 0.4.4 → 0.4.5
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 +1 -0
- data/HISTORY +6 -0
- data/README.md +18 -13
- data/VERSION +1 -1
- data/firefly.gemspec +6 -2
- data/lib/firefly.rb +2 -0
- data/lib/firefly/server.rb +73 -2
- data/public/style.css +4 -1
- data/spec/files/export.csv +3 -0
- data/spec/files/export.xml +16 -0
- data/spec/files/export.yml +13 -0
- data/spec/firefly/api_spec.rb +37 -1
- data/spec/fixtures/urls.yml +11 -0
- data/spec/spec_helper.rb +14 -1
- data/views/index.haml +20 -6
- data/views/layout.haml +1 -1
- metadata +8 -4
data/.gitignore
CHANGED
data/HISTORY
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= HEAD
|
2
|
+
|
3
|
+
* 2010-08-10 - Added sorting of shortened URLs. Closes #12.
|
4
|
+
* 2010-08-10 - Added CSV, XML and YAML export of all shortened URLs. Closes #11.
|
5
|
+
* 2010-08-10 - Updated bookmarklet JavaScript to escape URL-unsafe charachters in the API key. Fixes #17
|
6
|
+
|
1
7
|
= 0.4.4 / 2010-06-20
|
2
8
|
|
3
9
|
* 2010-06-20 - Updated gem dependencies for DataMapper 1.0.0
|
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# FireFly
|
2
2
|
|
3
|
-
FireFly is a simple URL shortener for personal use.
|
3
|
+
FireFly is a simple URL shortener for personal (or not so personal) use.
|
4
|
+
|
5
|
+
# Quick-Start (2 minutes) with Heroku
|
6
|
+
|
7
|
+
See the [screencast][1] or [written instructions][2] on how to setup Firefly within 2 minutes on [Heroku][3]
|
4
8
|
|
5
9
|
# Installation
|
6
10
|
|
@@ -54,14 +58,12 @@ All configuration is done in `config.ru`.
|
|
54
58
|
|
55
59
|
* `:hostname` sets the hostname to use for shortened URLs.
|
56
60
|
* `:api_key` sets the API key. This key is required when posting new URLs
|
57
|
-
* `:database` a database URI that [DataMapper][
|
61
|
+
* `:database` a database URI that [DataMapper][4] can understand.
|
58
62
|
* `:recent_urls` sets the number of URLs to show in the overview. Default: 25.
|
59
63
|
* `:tweet` set the template to use for tweets. Default: `"Check this out: %short_url%"`
|
60
64
|
|
61
65
|
It's possible to use all kinds of backends with DataMapper. Sqlite3 and MySQL have been tested, but others may work as well.
|
62
66
|
|
63
|
-
[1]: http://datamapper.org/
|
64
|
-
|
65
67
|
# Usage
|
66
68
|
|
67
69
|
Simply visit `http://:hostname/` and enter your `:api_key`. You can now shorten URLs.
|
@@ -93,22 +95,25 @@ After you restart Terminal.app (or at least reload the `.profile` file) you can
|
|
93
95
|
|
94
96
|
# Bugs, Feature Requests, etc.
|
95
97
|
|
96
|
-
* [Source][
|
97
|
-
* [Issue tracker][
|
98
|
-
|
99
|
-
[2]: http://github.com/ariejan/firefly
|
100
|
-
[3]: http://github.com/ariejan/firefly/issues
|
98
|
+
* [Source][5]
|
99
|
+
* [Issue tracker][6]
|
101
100
|
|
102
101
|
Feel free to fork Firefly and create patches for it. Here are some basic instructions:
|
103
102
|
|
104
|
-
* [Fork][
|
103
|
+
* [Fork][7] Firefly
|
105
104
|
* Create a topic branch - `git checkout -b my_branch`
|
106
105
|
* Push to your branch - `git push origin my_branch`
|
107
|
-
* Create an [Issue][
|
106
|
+
* Create an [Issue][8] with a link to your branch
|
108
107
|
* That's it!
|
109
108
|
|
110
|
-
[
|
111
|
-
[
|
109
|
+
[1]: http://ariejan.net/2010/07/12/screencast-firefly-url-shortener-in-less-than-25-minutes/
|
110
|
+
[2]: http://ariejan.net/2010/06/06/setup-your-own-firefly-url-shortener-in-25-minutes/
|
111
|
+
[3]: http://heroku.com
|
112
|
+
[4]: http://datamapper.org/
|
113
|
+
[5]: http://github.com/ariejan/firefly
|
114
|
+
[6]: http://github.com/ariejan/firefly/issues
|
115
|
+
[7]: http://help.github.com/forking/
|
116
|
+
[8]: http://github.com/ariejan/firefly/issues
|
112
117
|
|
113
118
|
# License
|
114
119
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.5
|
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.5"
|
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-
|
12
|
+
s.date = %q{2010-08-10}
|
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 = [
|
@@ -36,10 +36,14 @@ Gem::Specification.new do |s|
|
|
36
36
|
"public/jquery-1.4.2.min.js",
|
37
37
|
"public/reset.css",
|
38
38
|
"public/style.css",
|
39
|
+
"spec/files/export.csv",
|
40
|
+
"spec/files/export.xml",
|
41
|
+
"spec/files/export.yml",
|
39
42
|
"spec/firefly/api_spec.rb",
|
40
43
|
"spec/firefly/base62_spec.rb",
|
41
44
|
"spec/firefly/server_spec.rb",
|
42
45
|
"spec/firefly/url_spec.rb",
|
46
|
+
"spec/fixtures/urls.yml",
|
43
47
|
"spec/spec.opts",
|
44
48
|
"spec/spec_helper.rb",
|
45
49
|
"views/index.haml",
|
data/lib/firefly.rb
CHANGED
data/lib/firefly/server.rb
CHANGED
@@ -94,7 +94,12 @@ module Firefly
|
|
94
94
|
get '/' do
|
95
95
|
@highlight = Firefly::Url.first(:code => params[:highlight])
|
96
96
|
@error = params[:highlight] == "error"
|
97
|
-
|
97
|
+
|
98
|
+
sort_column = params[:s] || 'created_at'
|
99
|
+
sort_order = params[:d] || 'desc'
|
100
|
+
|
101
|
+
@urls = Firefly::Url.all(:limit => config[:recent_urls], :order => [ sort_column.to_sym.send(sort_order.to_sym) ] )
|
102
|
+
|
98
103
|
haml :index
|
99
104
|
end
|
100
105
|
|
@@ -142,7 +147,73 @@ module Firefly
|
|
142
147
|
haml :info
|
143
148
|
end
|
144
149
|
end
|
145
|
-
|
150
|
+
|
151
|
+
# GET /api/export.csv
|
152
|
+
#
|
153
|
+
# Download a CSV file with all shortened URLs
|
154
|
+
get '/api/export.csv' do
|
155
|
+
validate_api_permission or return "Permission denied: Invalid API key"
|
156
|
+
|
157
|
+
@urls = Firefly::Url.all(:order => [ :created_at.asc ])
|
158
|
+
|
159
|
+
output = "\"Code\",\"Short URL\",\"Long URL\",\"Clicks\",\"Created at\"\n"
|
160
|
+
@urls.each do |url|
|
161
|
+
output += "\"#{url.code}\",\"#{short_url(url)}\",\"#{url.url}\",\"#{url.clicks}\",\"#{url.created_at.strftime('%Y-%m-%d %H:%M:%S')}\"\n"
|
162
|
+
end
|
163
|
+
|
164
|
+
attachment "firefly-export.csv"
|
165
|
+
content_type "text/csv"
|
166
|
+
output
|
167
|
+
end
|
168
|
+
|
169
|
+
# GET /api/export.xml
|
170
|
+
#
|
171
|
+
# Download a XML file with all shortened URLs
|
172
|
+
get '/api/export.xml' do
|
173
|
+
validate_api_permission or return "Permission denied: Invalid API key"
|
174
|
+
|
175
|
+
@urls = Firefly::Url.all(:order => [ :created_at.asc ])
|
176
|
+
|
177
|
+
# I know, manual XML creation is ugly, at least you don't need nokogiri
|
178
|
+
output = "<urls>\n"
|
179
|
+
@urls.each do |url|
|
180
|
+
output += " <url>\n"
|
181
|
+
output += " <code>#{url.code}</code>\n"
|
182
|
+
output += " <short_url>#{short_url(url)}</short_url>\n"
|
183
|
+
output += " <long_url>#{url.url}</long_url>\n"
|
184
|
+
output += " <clicks>#{url.clicks}</clicks>\n"
|
185
|
+
output += " <created_at>#{url.created_at.strftime('%Y-%m-%d %H:%M:%S')}</created_at>\n"
|
186
|
+
output += " </url>\n"
|
187
|
+
end
|
188
|
+
output += "</urls>\n"
|
189
|
+
|
190
|
+
attachment "firefly-export.xml"
|
191
|
+
content_type "text/xml"
|
192
|
+
output
|
193
|
+
end
|
194
|
+
|
195
|
+
# GET /api/export.yml
|
196
|
+
#
|
197
|
+
# Download a YAML file with all shortened URLs
|
198
|
+
get '/api/export.yml' do
|
199
|
+
validate_api_permission or return "Permission denied: Invalid API key"
|
200
|
+
|
201
|
+
@urls = Firefly::Url.all(:order => [ :created_at.asc ])
|
202
|
+
|
203
|
+
output = {}
|
204
|
+
@urls.each do |url|
|
205
|
+
output[url.code] = { 'code' => url.code,
|
206
|
+
'short_url' => short_url(url),
|
207
|
+
'long_url' => url.url,
|
208
|
+
'clicks' => url.clicks,
|
209
|
+
'created_at' => url.created_at.strftime('%Y-%m-%d %H:%M:%S') }
|
210
|
+
end
|
211
|
+
|
212
|
+
attachment "firefly-export.yml"
|
213
|
+
content_type "text/yaml"
|
214
|
+
YAML::dump(output)
|
215
|
+
end
|
216
|
+
|
146
217
|
# GET /b3d
|
147
218
|
#
|
148
219
|
# Redirect to the shortened URL
|
data/public/style.css
CHANGED
@@ -87,5 +87,8 @@ body { padding:0; margin:0; }
|
|
87
87
|
#main table tr td.value.fill { width: 100%; white-space: normal; }
|
88
88
|
#main table tr td.value.center { text-align: center; }
|
89
89
|
#main table tr td.label { font-weight: bold; }
|
90
|
+
#main table tr td.label a { text-decoration: none; font-size: 11px; }
|
91
|
+
#main table tr td.label a.highlight { color: #f00; }
|
92
|
+
|
90
93
|
#main table tr td input.short_url { border: 1px solid #CCC; color: #666; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 11px; height: 16px; padding: 3px 5px 2px; width: 160px; }
|
91
|
-
#main table tr td img.twitter { border: 0; margin: 0; padding: 0; float: right; width: 16px; height: 16px; }
|
94
|
+
#main table tr td img.twitter { border: 0; margin: 0; padding: 0; float: right; width: 16px; height: 16px; }
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<urls>
|
2
|
+
<url>
|
3
|
+
<code>def</code>
|
4
|
+
<short_url>http://test.host/def</short_url>
|
5
|
+
<long_url>http://example.org/</long_url>
|
6
|
+
<clicks>456</clicks>
|
7
|
+
<created_at>2010-02-24 14:55:00</created_at>
|
8
|
+
</url>
|
9
|
+
<url>
|
10
|
+
<code>abc</code>
|
11
|
+
<short_url>http://test.host/abc</short_url>
|
12
|
+
<long_url>http://example.com/</long_url>
|
13
|
+
<clicks>123</clicks>
|
14
|
+
<created_at>2010-04-01 12:00:00</created_at>
|
15
|
+
</url>
|
16
|
+
</urls>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
---
|
2
|
+
abc:
|
3
|
+
created_at: 2010-04-01 12:00:00
|
4
|
+
long_url: http://example.com/
|
5
|
+
short_url: http://test.host/abc
|
6
|
+
code: abc
|
7
|
+
clicks: 123
|
8
|
+
def:
|
9
|
+
created_at: 2010-02-24 14:55:00
|
10
|
+
long_url: http://example.org/
|
11
|
+
short_url: http://test.host/def
|
12
|
+
code: def
|
13
|
+
clicks: 456
|
data/spec/firefly/api_spec.rb
CHANGED
@@ -69,7 +69,7 @@ describe "API" do
|
|
69
69
|
|
70
70
|
it "should create a new Firefly::Url" do
|
71
71
|
lambda {
|
72
|
-
self.send verb, '/api/add', :url => 'http://example.org', :api_key => 'test'
|
72
|
+
self.send verb, '/api/add', :url => 'http://example.org/', :api_key => 'test'
|
73
73
|
}.should change(Firefly::Url, :count).by(1)
|
74
74
|
end
|
75
75
|
|
@@ -119,4 +119,40 @@ describe "API" do
|
|
119
119
|
last_response.status.should be(401)
|
120
120
|
end
|
121
121
|
end
|
122
|
+
|
123
|
+
describe "exports" do
|
124
|
+
before(:each) do
|
125
|
+
load_fixtures
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should export in CSV" do
|
129
|
+
get '/api/export.csv', :api_key => "test"
|
130
|
+
last_response.body.should eql(spec_file('export.csv'))
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should export in XML" do
|
134
|
+
get '/api/export.xml', :api_key => "test"
|
135
|
+
last_response.body.should eql(spec_file('export.xml'))
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should export in YAML" do
|
139
|
+
get '/api/export.yml', :api_key => "test"
|
140
|
+
last_response.body.should eql(spec_file('export.yml'))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "api key" do
|
145
|
+
def app
|
146
|
+
Firefly::Server.new do
|
147
|
+
set :hostname, "test.host"
|
148
|
+
set :api_key, "test#!"
|
149
|
+
set :database, "sqlite3://#{Dir.pwd}/firefly_test.sqlite3"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should be okay adding a new URL" do
|
154
|
+
self.send :get, '/api/add', :url => 'http://example.org/api_key_test', :api_key => 'test#!'
|
155
|
+
last_response.should be_ok
|
156
|
+
end
|
157
|
+
end
|
122
158
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,6 +6,7 @@ require 'rack/test'
|
|
6
6
|
require 'spec'
|
7
7
|
require 'spec/autorun'
|
8
8
|
require 'spec/interop/test'
|
9
|
+
require 'yaml'
|
9
10
|
|
10
11
|
# set test environment
|
11
12
|
set :environment, :test
|
@@ -37,4 +38,16 @@ Spec::Runner.configure do |config|
|
|
37
38
|
r.adapter.push_transaction(transaction)
|
38
39
|
end
|
39
40
|
end
|
40
|
-
|
41
|
+
|
42
|
+
# Loads the urls.yml fixtures.
|
43
|
+
def load_fixtures
|
44
|
+
Firefly::Url.destroy
|
45
|
+
urls = YAML::load(File.open('spec/fixtures/urls.yml'))
|
46
|
+
urls.each { |key, url| Firefly::Url.create(url) }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Load a spec file and return its contents
|
50
|
+
def spec_file(filename)
|
51
|
+
File.open('spec/files/'+filename) { |f| f.read }
|
52
|
+
end
|
53
|
+
end
|
data/views/index.haml
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
%p
|
7
7
|
Drag the following link to your bookmarks. Click it to shorten the URL to the page you're currently viewing.
|
8
8
|
%p
|
9
|
-
%a{ :href => "javascript:var%20d=document,w=window,enc=encodeURIComponent,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),s2=((s.toString()=='')?s:('%22'+enc(s)+'%22')),f='http://#{@config[:hostname]}/api/add',l=d.location,p='?visual=1&api_key
|
9
|
+
%a{ :href => "javascript:var%20d=document,w=window,enc=encodeURIComponent,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),s2=((s.toString()=='')?s:('%22'+enc(s)+'%22')),key=enc('#{@config[:api_key]}'),f='http://#{@config[:hostname]}/api/add',l=d.location,p='?visual=1&api_key='+key+'&url='+enc(l.href),u=f+p;try{if(!/^(.*\.)?tumblrzzz[^.]*$/.test(l.host))throw(0);tstbklt();}catch(z){a%20=function(){if(!w.open(u))l.href=u;};if(/Firefox/.test(navigator.userAgent))setTimeout(a,0);else%20a();}void(0)" } Shorten with #{@config[:hostname]}
|
10
10
|
|
11
11
|
%h1 How about shortening a URL?
|
12
12
|
|
@@ -23,6 +23,8 @@
|
|
23
23
|
|
24
24
|
%p
|
25
25
|
The URL you posted is invalid. Please post a valid HTTP url, including the protocol (http://) prefix.
|
26
|
+
|
27
|
+
%p= @url
|
26
28
|
|
27
29
|
- if @highlight
|
28
30
|
%h2 Your short URL
|
@@ -48,10 +50,10 @@
|
|
48
50
|
|
49
51
|
%table
|
50
52
|
%tr
|
51
|
-
%td.label Short URL
|
52
|
-
%td.label Full URL
|
53
|
-
%td.label Clicks
|
54
|
-
%td.label Shortened
|
53
|
+
%td.label(nowrap='nowrap') Short URL <a href="/?s=code&d=asc">▲</a> <a href="/?s=code&d=desc">▼</a>
|
54
|
+
%td.label(nowrap='nowrap') Full URL <a href="/?s=url&d=asc">▲</a> <a href="/?s=url&d=desc">▼</a>
|
55
|
+
%td.label(nowrap='nowrap') Clicks <a href="/?s=clicks&d=asc">▲</a> <a href="/?s=clicks&d=desc">▼</a>
|
56
|
+
%td.label(nowrap='nowrap') Shortened At <a href="/?s=created_at&d=asc">▲</a> <a href="/?s=created_at&d=desc">▼</a>
|
55
57
|
%td.label
|
56
58
|
- @urls.each do |url|
|
57
59
|
%tr{ :class => is_highlighted?(url) ? 'highlighted' : '' }
|
@@ -69,6 +71,18 @@
|
|
69
71
|
$('input.short_url').each(function(index) {
|
70
72
|
$(this).mouseup(function() { $(this).select(); });
|
71
73
|
});
|
74
|
+
|
75
|
+
if (window.location.search == "") {
|
76
|
+
var pathname = window.location.pathname + '?s=created_at&d=desc';
|
77
|
+
} else {
|
78
|
+
var pathname = window.location.pathname + window.location.search;
|
79
|
+
}
|
80
|
+
|
81
|
+
$('#main table tr td.label a').each(function(index) {
|
82
|
+
if ($(this).attr('href') == pathname) {
|
83
|
+
$(this).addClass('highlight');
|
84
|
+
}
|
85
|
+
});
|
72
86
|
});
|
73
87
|
- else
|
74
88
|
%h1 Please enter your API key
|
@@ -81,4 +95,4 @@
|
|
81
95
|
%label{ :for => 'api_key' } API Key
|
82
96
|
%input{ :type => 'password', :name => 'api_key', :id => 'api_key' }
|
83
97
|
%p
|
84
|
-
%input{ :type => 'submit', :name => 'submit', :id => 'submit', :value => "Let me in" }
|
98
|
+
%input{ :type => 'submit', :name => 'submit', :id => 'submit', :value => "Let me in" }
|
data/views/layout.haml
CHANGED
@@ -17,4 +17,4 @@
|
|
17
17
|
|
18
18
|
#footer
|
19
19
|
%p
|
20
|
-
Powered by <a href="http://github.com/ariejan/firefly">Firefly</a> v#{Firefly::Version} | <a href="http://github.com/ariejan/firefly/tree/v#{Firefly::Version}">Source</a> | <a href="http://github.com/ariejan/firefly/issues">Issues</a>
|
20
|
+
Powered by <a href="http://github.com/ariejan/firefly">Firefly</a> v#{Firefly::Version} | <a href="http://github.com/ariejan/firefly/tree/v#{Firefly::Version}">Source</a> | <a href="http://github.com/ariejan/firefly/issues">Issues</a> | Export <a href="/api/export.csv">CSV</a>, <a href="/api/export.yml">YAML</a> or <a href="/api/export.xml">XML</a>
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: firefly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
9
|
+
- 5
|
10
|
+
version: 0.4.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ariejan de Vroom
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-08-10 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -190,10 +190,14 @@ files:
|
|
190
190
|
- public/jquery-1.4.2.min.js
|
191
191
|
- public/reset.css
|
192
192
|
- public/style.css
|
193
|
+
- spec/files/export.csv
|
194
|
+
- spec/files/export.xml
|
195
|
+
- spec/files/export.yml
|
193
196
|
- spec/firefly/api_spec.rb
|
194
197
|
- spec/firefly/base62_spec.rb
|
195
198
|
- spec/firefly/server_spec.rb
|
196
199
|
- spec/firefly/url_spec.rb
|
200
|
+
- spec/fixtures/urls.yml
|
197
201
|
- spec/spec.opts
|
198
202
|
- spec/spec_helper.rb
|
199
203
|
- views/index.haml
|