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 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.0
1
+ 1.2.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{firefly}
8
- s.version = "1.1.0"
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{2010-10-02}
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
@@ -7,8 +7,12 @@ require 'dm-core'
7
7
  require 'dm-migrations'
8
8
  require 'dm-transactions'
9
9
  require 'dm-aggregates'
10
- require 'barby'
11
- require 'barby/outputter/png_outputter'
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__)))
@@ -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 = params[:url]
119
- @code, @result = generate_short_url(@url)
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
- # GET /b3d.png
218
- #
219
- # Return a QR code image
220
- get '/:code.png' do
221
- @url = Firefly::Url.first(:code => params[:code])
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
- if @url.nil?
224
- status 404
225
- "Sorry, that code is unknown."
226
- else
227
- qr = Barby::QrCode.new(short_url(@url))
228
- content_type('image/png')
229
- cache_control :public, :max_age => 2592000 # One month
230
- body(qr.to_png(:xdim => 15, :margin => 30))
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
 
@@ -2,11 +2,12 @@ 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
+ 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 => 16
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
- the_url.update(:code => Firefly::Base62.encode(the_url.id.to_i)) if the_url.code.nil?
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
@@ -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
 
@@ -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
- it "should return a 200 status" do
23
- get '/alpha.png'
24
- last_response.should be_ok
25
- end
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
- it "should return a PNG image" do
28
- get '/alpha.png'
29
- last_response.headers['Content-Type'].should eql('image/png')
30
- end
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
- it "should cache-control to 1 month" do
33
- get '/alpha.png'
34
- last_response.headers['Cache-Control'].should eql('public, max-age=2592000')
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: 19
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 1.1.0
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: 2010-10-02 00:00:00 +02:00
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: &id010 !ruby/object:Gem::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: *id010
147
+ version_requirements: *id008
180
148
  - !ruby/object:Gem::Dependency
181
149
  name: rack-test
182
150
  prerelease: false
183
- requirement: &id011 !ruby/object:Gem::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: *id011
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: []