firefly 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -0
- data/Rakefile +0 -2
- data/VERSION +1 -1
- data/firefly.gemspec +2 -8
- data/lib/firefly.rb +6 -2
- data/lib/firefly/server.rb +23 -17
- data/lib/firefly/url.rb +15 -6
- data/spec/firefly/api_spec.rb +21 -0
- data/spec/firefly/server_spec.rb +18 -15
- metadata +8 -40
data/README.md
CHANGED
@@ -52,6 +52,14 @@ Next you can start your web server. You may try thin:
|
|
52
52
|
|
53
53
|
Now visit `http://:hostname/` and enter your `:api_key`. Happy URL shortening!
|
54
54
|
|
55
|
+
# Enabling QR Codes
|
56
|
+
|
57
|
+
To enable QR Codes install the `barby` and `png` gems:
|
58
|
+
|
59
|
+
gem install barby png
|
60
|
+
|
61
|
+
Firefly will detect you have these gems available and enable QR Code support automatically.
|
62
|
+
|
55
63
|
# Configuration
|
56
64
|
|
57
65
|
All configuration is done in `config.ru`.
|
@@ -115,6 +123,11 @@ Feel free to fork Firefly and create patches for it. Here are some basic instruc
|
|
115
123
|
[7]: http://help.github.com/forking/
|
116
124
|
[8]: http://github.com/ariejan/firefly/issues
|
117
125
|
|
126
|
+
# Contributors
|
127
|
+
|
128
|
+
* Ariejan de Vroom - Original author
|
129
|
+
* Matthew Boeh - Contributor
|
130
|
+
|
118
131
|
# License
|
119
132
|
|
120
133
|
Copyright (c) 2009 Ariejan de Vroom
|
data/Rakefile
CHANGED
@@ -18,8 +18,6 @@ begin
|
|
18
18
|
gemspec.add_dependency('dm-aggregates', '~> 1.0.2')
|
19
19
|
gemspec.add_dependency('dm-sqlite-adapter', '~> 1.0.2')
|
20
20
|
gemspec.add_dependency('haml', '~> 3.0.18')
|
21
|
-
gemspec.add_dependency('barby', '~> 0.4.0')
|
22
|
-
gemspec.add_dependency('png', '1.1.0')
|
23
21
|
|
24
22
|
gemspec.add_development_dependency('rspec', '~> 1.3.0')
|
25
23
|
gemspec.add_development_dependency('rack-test', '~> 0.5.4')
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
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 = "1.
|
8
|
+
s.version = "1.2.0"
|
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{
|
12
|
+
s.date = %q{2011-01-26}
|
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 = [
|
@@ -76,8 +76,6 @@ Gem::Specification.new do |s|
|
|
76
76
|
s.add_runtime_dependency(%q<dm-aggregates>, ["~> 1.0.2"])
|
77
77
|
s.add_runtime_dependency(%q<dm-sqlite-adapter>, ["~> 1.0.2"])
|
78
78
|
s.add_runtime_dependency(%q<haml>, ["~> 3.0.18"])
|
79
|
-
s.add_runtime_dependency(%q<barby>, ["~> 0.4.0"])
|
80
|
-
s.add_runtime_dependency(%q<png>, ["= 1.1.0"])
|
81
79
|
s.add_development_dependency(%q<rspec>, ["~> 1.3.0"])
|
82
80
|
s.add_development_dependency(%q<rack-test>, ["~> 0.5.4"])
|
83
81
|
else
|
@@ -88,8 +86,6 @@ Gem::Specification.new do |s|
|
|
88
86
|
s.add_dependency(%q<dm-aggregates>, ["~> 1.0.2"])
|
89
87
|
s.add_dependency(%q<dm-sqlite-adapter>, ["~> 1.0.2"])
|
90
88
|
s.add_dependency(%q<haml>, ["~> 3.0.18"])
|
91
|
-
s.add_dependency(%q<barby>, ["~> 0.4.0"])
|
92
|
-
s.add_dependency(%q<png>, ["= 1.1.0"])
|
93
89
|
s.add_dependency(%q<rspec>, ["~> 1.3.0"])
|
94
90
|
s.add_dependency(%q<rack-test>, ["~> 0.5.4"])
|
95
91
|
end
|
@@ -101,8 +97,6 @@ Gem::Specification.new do |s|
|
|
101
97
|
s.add_dependency(%q<dm-aggregates>, ["~> 1.0.2"])
|
102
98
|
s.add_dependency(%q<dm-sqlite-adapter>, ["~> 1.0.2"])
|
103
99
|
s.add_dependency(%q<haml>, ["~> 3.0.18"])
|
104
|
-
s.add_dependency(%q<barby>, ["~> 0.4.0"])
|
105
|
-
s.add_dependency(%q<png>, ["= 1.1.0"])
|
106
100
|
s.add_dependency(%q<rspec>, ["~> 1.3.0"])
|
107
101
|
s.add_dependency(%q<rack-test>, ["~> 0.5.4"])
|
108
102
|
end
|
data/lib/firefly.rb
CHANGED
@@ -7,8 +7,12 @@ require 'dm-core'
|
|
7
7
|
require 'dm-migrations'
|
8
8
|
require 'dm-transactions'
|
9
9
|
require 'dm-aggregates'
|
10
|
-
|
11
|
-
require 'barby
|
10
|
+
begin
|
11
|
+
require 'barby'
|
12
|
+
require 'barby/outputter/png_outputter'
|
13
|
+
rescue LoadError
|
14
|
+
|
15
|
+
end
|
12
16
|
|
13
17
|
$:.unshift(File.dirname(__FILE__)) unless
|
14
18
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
data/lib/firefly/server.rb
CHANGED
@@ -50,11 +50,11 @@ module Firefly
|
|
50
50
|
"http://#{config[:hostname]}/#{url.code}"
|
51
51
|
end
|
52
52
|
|
53
|
-
def generate_short_url(url = nil)
|
53
|
+
def generate_short_url(url = nil, requested_code = nil)
|
54
54
|
code, result = nil, nil
|
55
55
|
|
56
56
|
if !url.nil? && url != ""
|
57
|
-
ff_url = Firefly::Url.shorten(url)
|
57
|
+
ff_url = Firefly::Url.shorten(url, requested_code)
|
58
58
|
if !ff_url.nil?
|
59
59
|
code = ff_url.code
|
60
60
|
result = "http://#{config[:hostname]}/#{code}"
|
@@ -115,8 +115,10 @@ module Firefly
|
|
115
115
|
api_add = lambda {
|
116
116
|
validate_api_permission or return "Permission denied: Invalid API key"
|
117
117
|
|
118
|
-
@url
|
119
|
-
@
|
118
|
+
@url = params[:url]
|
119
|
+
@requested_code = params[:short]
|
120
|
+
@code, @result = generate_short_url(@url, @requested_code)
|
121
|
+
invalid = @result =~ /you posted is invalid/
|
120
122
|
@result ||= "Invalid URL specified."
|
121
123
|
|
122
124
|
if params[:visual]
|
@@ -124,6 +126,7 @@ module Firefly
|
|
124
126
|
@code ||= "error"
|
125
127
|
redirect "/?highlight=#{@code}"
|
126
128
|
else
|
129
|
+
head 422 if invalid
|
127
130
|
@result
|
128
131
|
end
|
129
132
|
}
|
@@ -214,20 +217,23 @@ module Firefly
|
|
214
217
|
YAML::dump(output)
|
215
218
|
end
|
216
219
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
220
|
+
|
221
|
+
if defined? Barby
|
222
|
+
# GET /b3d.png
|
223
|
+
#
|
224
|
+
# Return a QR code image
|
225
|
+
get '/:code.png' do
|
226
|
+
@url = Firefly::Url.first(:code => params[:code])
|
222
227
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
228
|
+
if @url.nil?
|
229
|
+
status 404
|
230
|
+
"Sorry, that code is unknown."
|
231
|
+
else
|
232
|
+
qr = Barby::QrCode.new(short_url(@url))
|
233
|
+
content_type('image/png')
|
234
|
+
cache_control :public, :max_age => 2592000 # One month
|
235
|
+
body(qr.to_png(:xdim => 15, :margin => 30))
|
236
|
+
end
|
231
237
|
end
|
232
238
|
end
|
233
239
|
|
data/lib/firefly/url.rb
CHANGED
@@ -2,11 +2,12 @@ module Firefly
|
|
2
2
|
class Url
|
3
3
|
include DataMapper::Resource
|
4
4
|
|
5
|
-
VALID_URL_REGEX
|
6
|
-
|
5
|
+
VALID_URL_REGEX = /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/ix
|
6
|
+
VALID_CODE_REGEX = /^[a-z0-9\-_]{3,64}$/i
|
7
|
+
|
7
8
|
property :id, Serial
|
8
9
|
property :url, String, :index => true, :length => 255
|
9
|
-
property :code, String, :index => true, :length =>
|
10
|
+
property :code, String, :index => true, :length => 64
|
10
11
|
property :clicks, Integer, :default => 0
|
11
12
|
property :created_at, DateTime, :default => Proc.new{Time.now}
|
12
13
|
|
@@ -16,13 +17,15 @@ module Firefly
|
|
16
17
|
end
|
17
18
|
|
18
19
|
# Shorten a long_url and return a new FireFly::Url
|
19
|
-
def self.shorten(long_url)
|
20
|
+
def self.shorten(long_url, code = nil)
|
20
21
|
return nil unless valid_url?(long_url)
|
21
|
-
|
22
|
+
return nil unless valid_code?(code)
|
23
|
+
|
22
24
|
long_url = normalize_url(long_url)
|
23
25
|
|
24
26
|
the_url = Firefly::Url.first(:url => long_url) || Firefly::Url.create(:url => long_url)
|
25
|
-
|
27
|
+
code ||= Firefly::Base62.encode(the_url.id.to_i)
|
28
|
+
the_url.update(:code => code) if the_url.code.nil?
|
26
29
|
the_url
|
27
30
|
end
|
28
31
|
|
@@ -36,5 +39,11 @@ module Firefly
|
|
36
39
|
def self.valid_url?(url)
|
37
40
|
url.match(Firefly::Url::VALID_URL_REGEX)
|
38
41
|
end
|
42
|
+
|
43
|
+
def self.valid_code?(code)
|
44
|
+
return true if code.nil?
|
45
|
+
code.match(Firefly::Url::VALID_CODE_REGEX) && Firefly::Url.count(:code => code) == 0
|
46
|
+
end
|
47
|
+
|
39
48
|
end
|
40
49
|
end
|
data/spec/firefly/api_spec.rb
CHANGED
@@ -21,6 +21,27 @@ describe "API" do
|
|
21
21
|
last_response.body.should eql("http://test.host/#{url.code}")
|
22
22
|
end
|
23
23
|
|
24
|
+
it "should permit including a requested short code" do
|
25
|
+
self.send verb, '/api/add', :url => "http://example.org", :short => 'orz', :api_key => 'test'
|
26
|
+
|
27
|
+
last_response.should be_ok
|
28
|
+
last_response.body.should eql("http://test.host/orz")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not permit too-short, too-long, or duplicate short codes" do
|
32
|
+
self.send verb, '/api/add', :url => "http://example.org", :short => 'orz', :api_key => 'test'
|
33
|
+
last_response.should be_ok
|
34
|
+
self.send verb, '/api/add', :url => "http://example.com", :short => 'orz', :api_key => 'test'
|
35
|
+
last_response.should_not be_ok
|
36
|
+
last_response.body.should match("The URL you posted is invalid")
|
37
|
+
self.send verb, '/api/add', :url => "http://example.org", :short => 'or', :api_key => 'test'
|
38
|
+
last_response.should_not be_ok
|
39
|
+
last_response.body.should match("The URL you posted is invalid")
|
40
|
+
self.send verb, '/api/add', :url => "http://example.org", :short => 'orz' * 37, :api_key => 'test'
|
41
|
+
last_response.should_not be_ok
|
42
|
+
last_response.body.should match("The URL you posted is invalid")
|
43
|
+
end
|
44
|
+
|
24
45
|
it "should show an error when shortening an invalid URL" do
|
25
46
|
self.send verb, '/api/add', :url => 'ftp://example.org', :api_key => 'test'
|
26
47
|
|
data/spec/firefly/server_spec.rb
CHANGED
@@ -14,24 +14,27 @@ describe "Firefly" do
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
describe "QR Codes" do
|
18
|
-
before(:each) do
|
19
|
-
@url = Firefly::Url.create(:url => 'http://example.com/123', :code => 'alpha')
|
20
|
-
end
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
if defined? Barby
|
19
|
+
describe "QR Codes" do
|
20
|
+
before(:each) do
|
21
|
+
@url = Firefly::Url.create(:url => 'http://example.com/123', :code => 'alpha')
|
22
|
+
end
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
it "should return a 200 status" do
|
25
|
+
get '/alpha.png'
|
26
|
+
last_response.should be_ok
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return a PNG image" do
|
30
|
+
get '/alpha.png'
|
31
|
+
last_response.headers['Content-Type'].should eql('image/png')
|
32
|
+
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
it "should cache-control to 1 month" do
|
35
|
+
get '/alpha.png'
|
36
|
+
last_response.headers['Cache-Control'].should eql('public, max-age=2592000')
|
37
|
+
end
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
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: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 1.
|
10
|
+
version: 1.2.0
|
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:
|
18
|
+
date: 2011-01-26 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -129,42 +129,10 @@ dependencies:
|
|
129
129
|
version: 3.0.18
|
130
130
|
type: :runtime
|
131
131
|
version_requirements: *id007
|
132
|
-
- !ruby/object:Gem::Dependency
|
133
|
-
name: barby
|
134
|
-
prerelease: false
|
135
|
-
requirement: &id008 !ruby/object:Gem::Requirement
|
136
|
-
none: false
|
137
|
-
requirements:
|
138
|
-
- - ~>
|
139
|
-
- !ruby/object:Gem::Version
|
140
|
-
hash: 15
|
141
|
-
segments:
|
142
|
-
- 0
|
143
|
-
- 4
|
144
|
-
- 0
|
145
|
-
version: 0.4.0
|
146
|
-
type: :runtime
|
147
|
-
version_requirements: *id008
|
148
|
-
- !ruby/object:Gem::Dependency
|
149
|
-
name: png
|
150
|
-
prerelease: false
|
151
|
-
requirement: &id009 !ruby/object:Gem::Requirement
|
152
|
-
none: false
|
153
|
-
requirements:
|
154
|
-
- - "="
|
155
|
-
- !ruby/object:Gem::Version
|
156
|
-
hash: 19
|
157
|
-
segments:
|
158
|
-
- 1
|
159
|
-
- 1
|
160
|
-
- 0
|
161
|
-
version: 1.1.0
|
162
|
-
type: :runtime
|
163
|
-
version_requirements: *id009
|
164
132
|
- !ruby/object:Gem::Dependency
|
165
133
|
name: rspec
|
166
134
|
prerelease: false
|
167
|
-
requirement: &
|
135
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
168
136
|
none: false
|
169
137
|
requirements:
|
170
138
|
- - ~>
|
@@ -176,11 +144,11 @@ dependencies:
|
|
176
144
|
- 0
|
177
145
|
version: 1.3.0
|
178
146
|
type: :development
|
179
|
-
version_requirements: *
|
147
|
+
version_requirements: *id008
|
180
148
|
- !ruby/object:Gem::Dependency
|
181
149
|
name: rack-test
|
182
150
|
prerelease: false
|
183
|
-
requirement: &
|
151
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
184
152
|
none: false
|
185
153
|
requirements:
|
186
154
|
- - ~>
|
@@ -192,7 +160,7 @@ dependencies:
|
|
192
160
|
- 4
|
193
161
|
version: 0.5.4
|
194
162
|
type: :development
|
195
|
-
version_requirements: *
|
163
|
+
version_requirements: *id009
|
196
164
|
description: FireFly is a simple URL shortner for personal use. It's powered by Sinatra and can be run with any Rack-capable web server.
|
197
165
|
email: ariejan@ariejan.net
|
198
166
|
executables: []
|