firefly 1.1.0 → 1.2.0
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/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: []
|