shortener 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -11,3 +11,4 @@ bin/*
11
11
  *.gem
12
12
  *.swp
13
13
  Gemfile.lock
14
+ .rvmrc
data/README.rdoc CHANGED
@@ -77,6 +77,22 @@ And to access those URLs:
77
77
 
78
78
  user.shortened_urls
79
79
 
80
+ === Shorten URLs in generated emails
81
+
82
+ You can register the included mail interceptor to shorten all links in the emails generated by your Rails app. For example, add to your mailer:
83
+
84
+ class MyMailer < ActionMailer::Base
85
+ register_interceptor Shortener::ShortenUrlInterceptor.new
86
+ end
87
+
88
+ This will replace all long URLs in the emails generated by MyMailer with shortened versions. The base URL for the shortener will be infered from the mailer's default_url_options. If you use a different hostname for your shortener, you can use:
89
+
90
+ class MyMailer < ActionMailer::Base
91
+ register_interceptor Shortener::ShortenUrlInterceptor.new :base_url => "http://shortener.host"
92
+ end
93
+
94
+ The interceptor supports a few more arguments, see the implementation for details.
95
+
80
96
  == Origins
81
97
 
82
98
  Shortener is based on code from Dealush[http://dealush.com], for a bit of backstory to Shortener see this {blog post}[http://jamespmcgrath.com/a-simple-link-shortener-in-rails/].
@@ -2,8 +2,11 @@ class Shortener::ShortenedUrlsController < ActionController::Base
2
2
 
3
3
  # find the real link for the shortened link key and redirect
4
4
  def show
5
+ # only use the leading valid characters
6
+ token = /^([#{Shortener.key_chars.join}]*).*/.match(params[:id])[1]
7
+
5
8
  # pull the link out of the db
6
- sl = ::Shortener::ShortenedUrl.find_by_unique_key(params[:id])
9
+ sl = ::Shortener::ShortenedUrl.find_by_unique_key(token)
7
10
 
8
11
  if sl
9
12
  # don't want to wait for the increment to happen, make it snappy!
data/lib/shortener.rb CHANGED
@@ -3,6 +3,7 @@ require "active_support/dependencies"
3
3
  module Shortener
4
4
 
5
5
  autoload :ActiveRecordExtension, "shortener/active_record_extension"
6
+ autoload :ShortenUrlInterceptor, "shortener/shorten_url_interceptor"
6
7
 
7
8
  CHARSETS = {
8
9
  :alphanum => ('a'..'z').to_a + (0..9).to_a,
@@ -6,6 +6,8 @@ class Shortener::Railtie < ::Rails::Railtie #:nodoc:
6
6
  ActiveSupport.on_load :active_record do
7
7
  extend Shortener::ActiveRecordExtension
8
8
  end
9
- ActionView::Base.send :include, Shortener::ShortenerHelper
9
+ ActiveSupport.on_load :action_view do
10
+ include Shortener::ShortenerHelper
11
+ end
10
12
  end
11
13
  end
@@ -0,0 +1,63 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Shortener
3
+ =begin
4
+ The Shorten URL Interceptor is a mail interceptor which shortens any URLs
5
+ in generated emails.
6
+
7
+ Usage:
8
+
9
+ class MyMailer < ActionMailer::Base
10
+ register_interceptor Shortener::ShortenUrlInterceptor.new
11
+ end
12
+ =end
13
+ class ShortenUrlInterceptor
14
+
15
+ DEFAULT_NOT_SHORTEN = [ 'twitter\.com/',
16
+ 'facebook\.com/',
17
+ 'cloudfront\.net/' ].map {|r| Regexp.new(r) }
18
+ DEFAULT_LENGTH_THRESHOLD = 20
19
+
20
+ URL_REGEX = /\b((https?):\/\/\w+\.)[-A-Z0-9+&@#\/%?=~_|$!:,.;]*[-A-Z0-9+&@#\/%=~_|$]/i
21
+ MIME_TYPES = %w(text/plain text/html application/xhtml+xml)
22
+
23
+ def initialize(opts = {})
24
+ @not_shorten = opts[:not_shorten] || DEFAULT_NOT_SHORTEN
25
+ @length_threshold = opts[:length_threshold] || DEFAULT_LENGTH_THRESHOLD
26
+ @base_url = opts[:base_url] || ShortenUrlInterceptor.infer_base_url
27
+ end
28
+
29
+ def delivering_email(email)
30
+ [email, email.all_parts].flatten.compact.each do |part|
31
+ if MIME_TYPES.include?(part.mime_type)
32
+ part.body = part.body.decoded.gsub(URL_REGEX) do |url|
33
+ shorten_url(url)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def shorten_url(url)
42
+ if url.length > @length_threshold && ! @not_shorten.any?{|r| r =~ url}
43
+ short_url = Shortener::ShortenedUrl.generate!(url)
44
+ File.join(@base_url, short_url.unique_key)
45
+ else
46
+ url
47
+ end
48
+ rescue => err
49
+ raise err if Rails.env.test?
50
+ Airbrake.notify(err, :url => url) if defined?(Airbrake)
51
+ return url
52
+ end
53
+
54
+ def self.infer_base_url
55
+ host = ActionMailer::Base.default_url_options[:host]
56
+ if host.blank?
57
+ raise "Please supply :base_url for ShortenUrlInterceptor or define default_url_options for mailer"
58
+ else
59
+ "http://#{host}/"
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,3 +1,3 @@
1
1
  module Shortener
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,19 +1,40 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require 'spec_helper'
3
3
 
4
+ shared_examples_for "good code" do
5
+ it "redirects to actual url" do
6
+ get :show, :id => code
7
+ response.should redirect_to("http://www.doorkeeperhq.com/")
8
+ end
9
+ end
10
+
11
+ shared_examples_for "wrong code" do
12
+ it "redirects to actual url" do
13
+ get :show, :id => code
14
+ response.should redirect_to("/")
15
+ end
16
+ end
17
+
4
18
  describe Shortener::ShortenedUrlsController do
5
19
  let(:short_url) { Shortener::ShortenedUrl.generate("www.doorkeeperhq.com") }
6
20
 
7
- describe "GET show with good code" do
8
- it "redirects to actual url" do
9
- get :show, :id => short_url.unique_key
10
- response.should redirect_to("http://www.doorkeeperhq.com/")
11
- end
21
+ describe "GET show with actual code" do
22
+ let(:code) { short_url.unique_key}
23
+ it_should_behave_like "good code"
12
24
  end
25
+
26
+ describe "GET show with good code but trailing characters" do
27
+ let(:code) { "#{short_url.unique_key}-" }
28
+ it_should_behave_like "good code"
29
+ end
30
+
13
31
  describe "GET show with wrong code" do
14
- it "redirects to actual url" do
15
- get :show, :id => "testing"
16
- response.should redirect_to("/")
17
- end
32
+ let(:code) { "testing" }
33
+ it_should_behave_like "wrong code"
34
+ end
35
+
36
+ describe "GET show with code of invalid characters" do
37
+ let(:code) { "-" }
38
+ it_should_behave_like "wrong code"
18
39
  end
19
40
  end
@@ -1,10 +1,6 @@
1
1
  require File.expand_path('../boot', __FILE__)
2
2
 
3
- require "active_model/railtie"
4
- require "active_record/railtie"
5
- require "action_controller/railtie"
6
- require "action_view/railtie"
7
-
3
+ require "rails/all"
8
4
  Bundler.require
9
5
  require "shortener"
10
6
 
@@ -40,5 +36,7 @@ module Dummy
40
36
 
41
37
  # Configure sensitive parameters which will be filtered from the log file.
42
38
  config.filter_parameters += [:password]
39
+
40
+ config.action_mailer.default_url_options = { :host => "mbln.jp" }
43
41
  end
44
42
  end
@@ -0,0 +1,62 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'mail'
4
+
5
+ describe Shortener::ShortenUrlInterceptor do
6
+
7
+ def create_email(body)
8
+ Mail.new(:from => 'test@mbln.jp', :to => 'test@mbln.jp', :body => body).tap do |m|
9
+ m.encoded
10
+ Shortener::ShortenUrlInterceptor.new.delivering_email(m)
11
+ end
12
+ end
13
+
14
+ TEXTS = [ "Test with URL: %{url}",
15
+ "Test with URL: %{url}!",
16
+ "Test with URL: %{url}, hu!",
17
+ "Test with URL: %{url}. hu!",
18
+ "Test with URL: <a href='%{url}'>test</a>",
19
+ "Test with URL: <a href=\"%{url}\">test</a>" ]
20
+
21
+ shared_examples_for "shortens URL in text" do |url|
22
+ TEXTS.each do |raw_email_body_text|
23
+ email_body_text = raw_email_body_text % {:url => url}
24
+ it("shortens for #{email_body_text}") do
25
+ email = create_email(email_body_text)
26
+ short_url = Shortener::ShortenedUrl.find_by_url(url)
27
+ short_url.should_not be_nil
28
+ email.body.should == (raw_email_body_text % {:url => "http://mbln.jp/#{short_url.unique_key}"})
29
+ end
30
+ end
31
+ end
32
+
33
+ shared_examples_for "does not shorten URL" do |url|
34
+ TEXTS.each do |raw_email_body_text|
35
+ email_body_text = raw_email_body_text % {:url => url}
36
+ it("keeps URL for #{email_body_text}") do
37
+ email = create_email(email_body_text)
38
+ short_url = Shortener::ShortenedUrl.find_by_url(url)
39
+ short_url.should be_nil
40
+ email.body.should == email_body_text
41
+ end
42
+ end
43
+ end
44
+
45
+ [ "http://client.doorkeeper.jp/events/124-title",
46
+ "http://client.doorkeeper.jp/events/124-",
47
+ "http://client.doorkeeper.jp/events/124-title?auth_token=xabagangs",
48
+ "http://client.doorkeeper.jp/events/124-%E4%F6%A0",
49
+ ].each do |url|
50
+ it_should_behave_like "shortens URL in text", url
51
+ end
52
+
53
+ # we won't shorten certain URLs:
54
+ [ "http://t.co/asdvk", # short URL
55
+ "http://twitter.com/doorkeeper_app", # twitter URL
56
+ "http://facebook.com/doorkeeper_app", # facebook URL
57
+ "http://d1dqic1fklzs1z.cloudfront.net/assets/doorkeeper_group_normal-3a3292fd09e39a70084c247aef60cba9.gif" # asset URL
58
+ ].each do |url|
59
+ it_should_behave_like "does not shorten URL", url
60
+ end
61
+
62
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shortener
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-04-23 00:00:00.000000000Z
13
+ date: 2012-04-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
17
- requirement: &17116840 !ruby/object:Gem::Requirement
17
+ requirement: &70307977503340 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 3.0.7
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *17116840
25
+ version_requirements: *70307977503340
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: sqlite3
28
- requirement: &17116300 !ruby/object:Gem::Requirement
28
+ requirement: &70307977519260 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *17116300
36
+ version_requirements: *70307977519260
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rspec-rails
39
- requirement: &17115620 !ruby/object:Gem::Requirement
39
+ requirement: &70307977518620 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *17115620
47
+ version_requirements: *70307977518620
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: shoulda-matchers
50
- requirement: &17114280 !ruby/object:Gem::Requirement
50
+ requirement: &70307977517720 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,7 +55,7 @@ dependencies:
55
55
  version: '0'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *17114280
58
+ version_requirements: *70307977517720
59
59
  description: Shortener is a Rails Engine Gem that makes it easy to create and interpret
60
60
  shortened URLs on your own domain from within your Rails application. Once installed
61
61
  Shortener will generate, store URLS and "unshorten" shortened URLs for your applications
@@ -82,6 +82,7 @@ files:
82
82
  - lib/shortener/active_record_extension.rb
83
83
  - lib/shortener/engine.rb
84
84
  - lib/shortener/railtie.rb
85
+ - lib/shortener/shorten_url_interceptor.rb
85
86
  - lib/shortener/version.rb
86
87
  - shortener.gemspec
87
88
  - spec/controllers/shortened_urls_controller_spec.rb
@@ -113,6 +114,7 @@ files:
113
114
  - spec/dummy/script/rails
114
115
  - spec/helpers/shortener_helper_spec.rb
115
116
  - spec/models/shortened_url_spec.rb
117
+ - spec/shortener/shorten_url_interceptor_spec.rb
116
118
  - spec/spec_helper.rb
117
119
  homepage: http://jamespmcgrath.com/projects/shortener
118
120
  licenses: []
@@ -134,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
136
  version: 1.3.6
135
137
  requirements: []
136
138
  rubyforge_project: shortener
137
- rubygems_version: 1.8.10
139
+ rubygems_version: 1.8.17
138
140
  signing_key:
139
141
  specification_version: 3
140
142
  summary: Shortener is a Rails Engine that makes it easy to create shortened URLs for