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 CHANGED
@@ -1,4 +1,5 @@
1
1
  *.sqlite3
2
+ sinatra.log
2
3
  config.ru
3
4
  pkg
4
5
  .rvmrc
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][1] can understand.
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][2]
97
- * [Issue tracker][3]
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][4] Firefly
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][5] with a link to your branch
106
+ * Create an [Issue][8] with a link to your branch
108
107
  * That's it!
109
108
 
110
- [4]: http://help.github.com/forking/
111
- [5]: http://github.com/ariejan/firefly/issues
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.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.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-06-20}
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
@@ -1,5 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'open-uri'
3
+ require 'cgi'
4
+ require 'yaml'
3
5
  require 'sinatra'
4
6
  require 'dm-core'
5
7
  require 'dm-migrations'
@@ -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
- @urls = Firefly::Url.all(:limit => config[:recent_urls], :order => [ :created_at.desc ])
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,3 @@
1
+ "Code","Short URL","Long URL","Clicks","Created at"
2
+ "def","http://test.host/def","http://example.org/","456","2010-02-24 14:55:00"
3
+ "abc","http://test.host/abc","http://example.com/","123","2010-04-01 12:00:00"
@@ -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
@@ -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
@@ -0,0 +1,11 @@
1
+ one:
2
+ url: http://example.com/
3
+ code: abc
4
+ clicks: 123
5
+ created_at: 2010-04-01 12:00:00
6
+
7
+ two:
8
+ url: http://example.org/
9
+ code: def
10
+ clicks: 456
11
+ created_at: 2010-02-24 14:55:00
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
- end
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=#{@config[:api_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]}
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 at
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 &nbsp;
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: 7
4
+ hash: 5
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 4
9
- - 4
10
- version: 0.4.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-06-20 00:00:00 +02:00
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