redir 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ ruby '1.9.3'
4
+
5
+ gem 'mongoid', '~> 3.0.0'
6
+ gem 'rack'
7
+
8
+ group :test, :development do
9
+ gem 'rack-test', require: 'rack/test'
10
+ gem 'rspec'
11
+ gem 'factory_girl'
12
+ gem 'timecop'
13
+ gem 'jeweler'
14
+ gem 'simplecov'
15
+ gem 'yard'
16
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,64 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ activemodel (3.2.8)
5
+ activesupport (= 3.2.8)
6
+ builder (~> 3.0.0)
7
+ activesupport (3.2.8)
8
+ i18n (~> 0.6)
9
+ multi_json (~> 1.0)
10
+ builder (3.0.3)
11
+ diff-lcs (1.1.3)
12
+ factory_girl (4.1.0)
13
+ activesupport (>= 3.0.0)
14
+ git (1.2.5)
15
+ i18n (0.6.1)
16
+ jeweler (1.8.4)
17
+ bundler (~> 1.0)
18
+ git (>= 1.2.5)
19
+ rake
20
+ rdoc
21
+ json (1.7.5)
22
+ mongoid (3.0.6)
23
+ activemodel (~> 3.1)
24
+ moped (~> 1.1)
25
+ origin (~> 1.0)
26
+ tzinfo (~> 0.3.22)
27
+ moped (1.2.5)
28
+ multi_json (1.3.6)
29
+ origin (1.0.9)
30
+ rack (1.4.1)
31
+ rack-test (0.6.1)
32
+ rack (>= 1.0)
33
+ rake (0.9.2.2)
34
+ rdoc (3.12)
35
+ json (~> 1.4)
36
+ rspec (2.11.0)
37
+ rspec-core (~> 2.11.0)
38
+ rspec-expectations (~> 2.11.0)
39
+ rspec-mocks (~> 2.11.0)
40
+ rspec-core (2.11.1)
41
+ rspec-expectations (2.11.3)
42
+ diff-lcs (~> 1.1.3)
43
+ rspec-mocks (2.11.3)
44
+ simplecov (0.7.1)
45
+ multi_json (~> 1.0)
46
+ simplecov-html (~> 0.7.1)
47
+ simplecov-html (0.7.1)
48
+ timecop (0.5.3)
49
+ tzinfo (0.3.33)
50
+ yard (0.8.3)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ factory_girl
57
+ jeweler
58
+ mongoid (~> 3.0.0)
59
+ rack
60
+ rack-test
61
+ rspec
62
+ simplecov
63
+ timecop
64
+ yard
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Springbot
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ Redir
2
+ =====
3
+
4
+ There are many gems that allow you to create a short url and redirect a user to another website. But there are not many store information about the requests that use Mongo which is why we created redir.
5
+
6
+ ### Features
7
+ * Fully rack compliant
8
+ * Generates a unique shortened url for you
9
+ * Takes care of the mongo heavy lifting for you
10
+ * Stores all of the request header information to gather analytics
11
+ * Allows the addition to two sets of request parameters for conversion analytics
12
+
13
+ How to use it
14
+ -------------
15
+
16
+ Redir is currently under active development.
17
+
18
+ ### Rails
19
+ include the gem in your Gemfile
20
+ in your application.rb add
21
+
22
+ config.middleware.use Rack::Redir
23
+
24
+ in your environments/ development.rb, test.rb or production.rb file add.
25
+
26
+ Redirect.host = '<your hostname>/'
27
+
28
+ add a config/mongoid.yml file. Setup a local mongodb instance or use one from a cloud provider and add those configuration settings to your mongoid.yml file as described here http://mongoid.org/en/mongoid/docs/installation.html#configuration
29
+
30
+ Redir by default uses the /a route for redirect requests. IE: http://<your url>/a/<shortened_id> If you have another route that is already set at a you can change this by adding to your application.rb.
31
+
32
+ Redirect.path = '<new path>/'
33
+
34
+ To create a shortened Url in your code
35
+
36
+ Redir.create_shortened_url <your url>
37
+
38
+ The Redirect object is a Mongoid object class. Therefore you can search for redirect information based on it's attributes. In the case of Redirect those attributes are original_url and shortened_id. For example
39
+
40
+ results = Redirect.where(:original_url => www.polyglotprogramminginc.com).all
41
+
42
+ Mongoid results a Mongoid::Criteria object. To get at the results you can use something like
43
+
44
+ results.to_a
45
+
46
+ to get an array of results. See the mongoid documentation for further details. The shortened id is only the 8 character unique id of the shortened url. For example for http://www.springbot/a/ddffggrr ddffggrr would be the shortened_id. There is also a shortened_url and shortened_path method. See the redirect.rb docucumentation for more information. To get the full url that the user will be able to hit to be redirected use the shortened_url helper....ie
47
+
48
+ redir = Redirect.create_shortened_url <your url>
49
+ redir.shortened_url #the full shortened url that the user would hit to be directed back to <your url>
50
+
51
+ RedirectLog contains information about redirects that have been done. We are currently working on helper methods to make this easier, but for the time being you can get at that data by doing the following.
52
+
53
+ read the _id field from a redirect object that you want to get logging information for. IE:
54
+
55
+ my_redirect = Redirect.where(:shortened_id => 'rrffttee').to_a.last
56
+
57
+ and running a query against the RedirectLog object
58
+
59
+ redir_log = RedirectLog.where(:redirect_id => my_redirect._id)
60
+
61
+ an easier way to do this is
62
+
63
+ Redirector.find_logs <a shortened id>
64
+
65
+ you use the redir_log like any mongo object. the headers attribute contains the raw request headers and request time has the date/time the request came in.
66
+
67
+ ### Other configuration options
68
+
69
+ Redirect.append_tracking_info
70
+
71
+ adds redirect_mongo_id=<Redirect._id>&redirect_log_mongo_id=<RedirectLog._id> to the end of the url being redirected to for conversion tracking
72
+
73
+ Redirect.append_google_analytics
74
+
75
+ adds utm_source=<@@host>&utm_campaign=<Redirect._id>__<RedirectLog._id> to the end of the url being redirected to for google analytics tracking
76
+
77
+ Contributing
78
+ ------------
79
+
80
+ Just fork us on GitHub and send a pull request when you are ready.
81
+
82
+ License
83
+ -------
84
+
85
+ redir is released under the MIT license:
86
+
87
+ * http://www.opensource.org/licenses/MIT
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "redir"
18
+ gem.homepage = "http://github.com/springbot/redir"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A Mongo based url shortener and data gathering gem.}
21
+ gem.description = %Q{This gem adds functionality to shorten url's and gather data about requests going through it.}
22
+ gem.email = "lgleason@polyglotprogramminginc.com"
23
+ gem.authors = ["Lance Gleason"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ task :default => :spec
35
+
36
+ require 'yard'
37
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
@@ -0,0 +1,91 @@
1
+ class Redirect
2
+ include Mongoid::Document
3
+
4
+ field :original_url
5
+ field :shortened_id
6
+
7
+ @@host = 'http://localhost:3000/'
8
+ @@path = 'a/'
9
+ @@append_tracking_info = false
10
+ @@append_google_analytics = false
11
+
12
+ # Sets the value of the flag to determine if google analytics info should be sent with the url
13
+ # appends utm_source=<@@host>&utm_campaign=<Redirect._id>__<RedirectLog._id>
14
+ # @param [Boolean] value to set append_google_analytics flag to
15
+ def self.append_google_analytics= google_analytics_flag
16
+ @@append_google_analytics = google_analytics_flag
17
+ end
18
+
19
+ # Gets the value of the flag to determine if google analytics info should be sent with the url
20
+ # @returns [Boolean] value of the append_google_analytics flag
21
+ def self.append_google_analytics
22
+ @@append_google_analytics
23
+ end
24
+
25
+ # Sets the value of the flag to determine if tracking info should be sent with the url
26
+ # adds the following to the url being redirected to
27
+ # redirect_mongo_id=<Redirect._id>&redirect_log_mongo_id=<RedirectLog._id>
28
+ # @param [Boolean] value to set append_tracking_info flag to
29
+ def self.append_tracking_info= tracking_info_flag
30
+ @@append_tracking_info = tracking_info_flag
31
+ end
32
+
33
+ # Gets the value of the flag to determine if tracking info should be sent with the url
34
+ # @returns [Boolean] value of append_tracking_info flag
35
+ def self.append_tracking_info
36
+ @@append_tracking_info
37
+ end
38
+
39
+ # Sets the path to use in the shortened url
40
+ # @param [String] the path you want to use for your shortened url instead of the default a/
41
+ def self.path= path
42
+ @@path = path
43
+ end
44
+
45
+ # Gets the path for the shortened url
46
+ # @return [String] the path
47
+ def self.path
48
+ path = @@path.end_with?('/') ? @@path : "#{@@path}/"
49
+ path.start_with?('/') ? path[1..path.length] : path
50
+ end
51
+
52
+ # Sets the host to use in the shortened url
53
+ # @param [String] the host to use in the shotened url
54
+ def self.host= host
55
+ @@host = host
56
+ end
57
+
58
+ # Gets the host for the shortened url
59
+ # @return [String] the host for the shortened url
60
+ def self.host
61
+ @@host.end_with?('/') ? @@host : "#{@@host}/"
62
+ end
63
+
64
+ # Creates a shortened url id of 8 characters, creates and saves a redirect object with the
65
+ # new shortened url id.
66
+ # @param [String] the url to redirect to
67
+ # @return [Redirect] a saved redirect object with the newly created shortened url.
68
+ def self.create_shortened_url(original_url)
69
+ redir = self.new :original_url => original_url
70
+ shortened_id = SecureRandom.urlsafe_base64(6)
71
+ duplicate_find = Redirect.where(:shortened_id => shortened_id)
72
+ if duplicate_find and duplicate_find.last and (shortened_id == duplicate_find.last.shortened_id)
73
+ shortened_id = SecureRandom.urlsafe_base64(6)
74
+ end
75
+ redir.shortened_id = shortened_id
76
+ redir.save!
77
+ redir
78
+ end
79
+
80
+ # Returns the entire shortened url which consists of the host, path and shortened id
81
+ # @return [String] the shortened url
82
+ def shortened_url
83
+ "#{self.class.host}#{self.class.path}#{shortened_id}"
84
+ end
85
+
86
+ # Returns the shortened url path consisting of the path and shortened id
87
+ # @return [String] the shortened path
88
+ def shortened_path
89
+ "#{self.class.path}#{shortened_id}"
90
+ end
91
+ end
@@ -0,0 +1,7 @@
1
+ class RedirectLog
2
+ include Mongoid::Document
3
+
4
+ field :redirect_id
5
+ field :headers
6
+ field :request_time, type: DateTime
7
+ end
data/lib/rack/redir.rb ADDED
@@ -0,0 +1,51 @@
1
+ require 'mongoid'
2
+
3
+ module Rack
4
+ # Bounce those annoying favicon.ico requests
5
+ class Redir
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ env['PATH_INFO'] =~ %r{^a/(.+)$}
12
+ parsed_path = env['PATH_INFO'].split(/\//)
13
+ if "#{parsed_path.second}/" == Redirect.path
14
+ redir_asset = parsed_path.last
15
+ redirect_record = (Redirect.where shortened_id: redir_asset).to_a.last
16
+ headers = env.select {|k,v| k.start_with? 'HTTP_'}
17
+ if redirect_record and redirect_record.shortened_id == redir_asset
18
+ redirect_url = redirect_record.original_url.strip
19
+ redirect_to_url = redirect_url.start_with?('http') ? redirect_url : "http://#{redirect_url}"
20
+ redirect_log = RedirectLog.new(:redirect_id => redirect_record.id,
21
+ :headers => headers, :request_time => Time.now)
22
+ redirect_log.save!
23
+ if Redirect.append_tracking_info
24
+ redirect_to_url = append_parameter_separater redirect_to_url
25
+ redirect_to_url = "#{redirect_to_url}redirect_mongo_id=" +
26
+ "#{redirect_log.redirect_id}&redirect_log_mongo_id=#{redirect_log._id}"
27
+ end
28
+ if Redirect.append_google_analytics
29
+ redirect_to_url = append_parameter_separater redirect_to_url
30
+ redirect_to_url = "#{redirect_to_url}utm_source=#{Rack::Utils.escape Redirect.host}" +
31
+ "&utm_campaign=#{redirect_log.redirect_id}__#{redirect_log._id}"
32
+ end
33
+ [302, {'Location' => redirect_to_url}, self]
34
+ else
35
+ [404, {}, self]
36
+ end
37
+ else
38
+ @app.call env
39
+ end
40
+
41
+ end
42
+
43
+ def append_parameter_separater url
44
+ url.match('\?') ? "#{url}&" : "#{url}?"
45
+ end
46
+
47
+ def each(&block)
48
+ end
49
+ end
50
+ end
51
+
data/lib/redir.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'rack/redir'
2
+ require 'model/redirect'
3
+ require 'model/redirect_log'
4
+ require 'mongoid'
5
+ require 'redirector'
data/lib/redirector.rb ADDED
@@ -0,0 +1,18 @@
1
+ module Redirector
2
+ # finds logs associated with the shortened url
3
+ # @params [String] the shortened id to find logs for
4
+ # @returns [Array] the logs for the shortened id as an array
5
+ def Redirector.find_logs shortened_id
6
+ RedirectLog.where(:redirect_id => find_redirect_by_shortened_id(shortened_id)).to_a
7
+ end
8
+
9
+ # finds the number of times a redirect has been accessed
10
+ # @returns [Integer] the number of times the redirect has been accessed
11
+ def Redirector.number_of_redirect_hits shortened_id
12
+ RedirectLog.where(:redirect_id => find_redirect_by_shortened_id(shortened_id)).count
13
+ end
14
+
15
+ def Redirector.find_redirect_by_shortened_id shortened_id
16
+ Redirect.where(:shortened_id => shortened_id).to_a.last._id
17
+ end
18
+ end
data/redir.gemspec ADDED
@@ -0,0 +1,81 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "redir"
8
+ s.version = "0.0.4"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Lance Gleason"]
12
+ s.date = "2012-10-24"
13
+ s.description = "This gem adds functionality to shorten url's and gather data about requests going through it."
14
+ s.email = "lgleason@polyglotprogramminginc.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "LICENSE.txt",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/model/redirect.rb",
27
+ "lib/model/redirect_log.rb",
28
+ "lib/rack/redir.rb",
29
+ "lib/redir.rb",
30
+ "lib/redirector.rb",
31
+ "redir.gemspec",
32
+ "spec/factories/redirect.rb",
33
+ "spec/mongoid.yml",
34
+ "spec/spec_helper.rb",
35
+ "spec/unit/model/redirect_spec.rb",
36
+ "spec/unit/rack/redir_spec.rb",
37
+ "spec/unit/redirector_spec.rb"
38
+ ]
39
+ s.homepage = "http://github.com/springbot/redir"
40
+ s.licenses = ["MIT"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = "1.8.24"
43
+ s.summary = "A Mongo based url shortener and data gathering gem."
44
+
45
+ if s.respond_to? :specification_version then
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<mongoid>, ["~> 3.0.0"])
50
+ s.add_runtime_dependency(%q<rack>, [">= 0"])
51
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
52
+ s.add_development_dependency(%q<rspec>, [">= 0"])
53
+ s.add_development_dependency(%q<factory_girl>, [">= 0"])
54
+ s.add_development_dependency(%q<timecop>, [">= 0"])
55
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
56
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
57
+ s.add_development_dependency(%q<yard>, [">= 0"])
58
+ else
59
+ s.add_dependency(%q<mongoid>, ["~> 3.0.0"])
60
+ s.add_dependency(%q<rack>, [">= 0"])
61
+ s.add_dependency(%q<rack-test>, [">= 0"])
62
+ s.add_dependency(%q<rspec>, [">= 0"])
63
+ s.add_dependency(%q<factory_girl>, [">= 0"])
64
+ s.add_dependency(%q<timecop>, [">= 0"])
65
+ s.add_dependency(%q<jeweler>, [">= 0"])
66
+ s.add_dependency(%q<simplecov>, [">= 0"])
67
+ s.add_dependency(%q<yard>, [">= 0"])
68
+ end
69
+ else
70
+ s.add_dependency(%q<mongoid>, ["~> 3.0.0"])
71
+ s.add_dependency(%q<rack>, [">= 0"])
72
+ s.add_dependency(%q<rack-test>, [">= 0"])
73
+ s.add_dependency(%q<rspec>, [">= 0"])
74
+ s.add_dependency(%q<factory_girl>, [">= 0"])
75
+ s.add_dependency(%q<timecop>, [">= 0"])
76
+ s.add_dependency(%q<jeweler>, [">= 0"])
77
+ s.add_dependency(%q<simplecov>, [">= 0"])
78
+ s.add_dependency(%q<yard>, [">= 0"])
79
+ end
80
+ end
81
+
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :redirect do
3
+ sequence(:original_url) {|n| "http://www.google.com/#{n}"}
4
+ sequence(:shortened_id) {|n| "goo#{n}"}
5
+ end
6
+ end
data/spec/mongoid.yml ADDED
@@ -0,0 +1,29 @@
1
+ # Tell Mongoid which environment this configuration is for.
2
+ test:
3
+ # This starts the session configuration settings. You may have as
4
+ # many sessions as you like, but you must have at least 1 named
5
+ # 'default'.
6
+ sessions:
7
+ # Define the default session.
8
+ default:
9
+ # A session can have any number of hosts. Usually 1 for a single
10
+ # server setup, and at least 3 for a replica set. Hosts must be
11
+ # an array of host:port pairs. This session is single server.
12
+ hosts:
13
+ - localhost:27017
14
+ # Define the default database name.
15
+ database: www_test
16
+ # Since this database points at a session connected to MongoHQ, we must
17
+ # provide the authentication details.
18
+ options:
19
+ allow_dynamic_fields: true
20
+ identity_map_enabled: true
21
+ include_root_in_json: true
22
+ # Note this can also be true if you want to preload everything, but this is
23
+ # almost never necessary. Most of the time set this to false.
24
+ scope_overwrite_exception: true
25
+ raise_not_found_error: false
26
+ skip_version_check: false
27
+ use_activesupport_time_zone: false
28
+ use_utc: true
29
+
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'rack/test'
3
+ require 'bundler'
4
+ require 'mongoid'
5
+ require 'factory_girl'
6
+ require 'simplecov'
7
+ SimpleCov.start
8
+
9
+ Mongoid.load!('./spec/mongoid.yml', :test)
10
+
11
+ RSpec.configure do |config|
12
+ config.include FactoryGirl::Syntax::Methods
13
+ end
14
+
15
+ FactoryGirl.find_definitions
16
+
17
+ Dir['lib/**/*.rb'].each {|f| require File.join(File.dirname(__FILE__), '..', f.gsub(/.rb/, ''))}
18
+
19
+ module RedirHelper
20
+ require 'rack/test'
21
+ include Rack::Test::Methods
22
+
23
+ def app
24
+ test_app = lambda do |env|
25
+ [200, {}, 'test app']
26
+ end
27
+
28
+ Rack::Redir.new test_app
29
+ end
30
+ end
31
+
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+ include RedirHelper
3
+
4
+ describe :redirect do
5
+
6
+ after :each do
7
+ Redirect.delete_all
8
+ RedirectLog.delete_all
9
+ end
10
+
11
+ context 'redirect generator' do
12
+ context 'should generate a unique url' do
13
+ before :each do
14
+ SecureRandom.should_receive(:urlsafe_base64).with(6).and_return('eeeeee')
15
+ end
16
+
17
+ let(:redirect) { Redirect.create_shortened_url 'http://www.google.com' }
18
+ subject {redirect}
19
+ its (:shortened_id) {should eq 'eeeeee'}
20
+ its (:shortened_path) {should eq 'a/eeeeee'}
21
+ its (:shortened_url) {should eq 'http://localhost:3000/a/eeeeee'}
22
+ end
23
+
24
+
25
+ context 'duplicate redirects' do
26
+ before do
27
+ SecureRandom.should_receive(:urlsafe_base64).with(6).twice.and_return('eeeeee')
28
+ SecureRandom.should_receive(:urlsafe_base64).with(6).once.and_return('ffffff')
29
+ end
30
+
31
+ let(:redirect) { Redirect.create_shortened_url 'http://www.google.com' }
32
+ let(:second_redirect) { Redirect.create_shortened_url 'http://www.yahoo.com' }
33
+
34
+ it "should get a unique url on each request" do
35
+ redirect.shortened_id.should eq 'eeeeee'
36
+ second_redirect.shortened_id.should eq 'ffffff'
37
+ end
38
+ end
39
+ end
40
+
41
+ context 'leading and trailing slashes' do
42
+ context 'host' do
43
+ context 'no trailing slash' do
44
+ let (:redirect) {Redirect.create_shortened_url 'www.springbot.com'}
45
+
46
+ before do
47
+ Redirect.host = 'www.honeybadger.com'
48
+ SecureRandom.should_receive(:urlsafe_base64).with(6).at_most(:once).and_return('eeeeee')
49
+ end
50
+
51
+ context 'global host' do
52
+ subject {Redirect}
53
+ its (:host) {should eq 'www.honeybadger.com/'}
54
+ end
55
+
56
+ subject {redirect}
57
+ its (:shortened_url) {should eq 'www.honeybadger.com/a/eeeeee'}
58
+ end
59
+ end
60
+
61
+ context 'path' do
62
+
63
+ shared_examples_for 'a correct redirect' do
64
+ before {SecureRandom.should_receive(:urlsafe_base64).with(6).at_most(:once).and_return('eeeeee')}
65
+ let (:redirect) {Redirect.create_shortened_url 'www.springbot.com'}
66
+ context 'global host' do
67
+ subject {Redirect}
68
+ its (:path) {should eq 'badger/'}
69
+ end
70
+
71
+ subject {redirect}
72
+ its (:shortened_url) {should eq 'www.honeybadger.com/badger/eeeeee'}
73
+ its (:shortened_path) {should eq 'badger/eeeeee'}
74
+ end
75
+
76
+ context 'no trailing slash' do
77
+ before { Redirect.path = 'badger'}
78
+ it_behaves_like 'a correct redirect'
79
+ end
80
+
81
+ context 'no trailing slash and slash at the beginning' do
82
+ before { Redirect.path = '/badger' }
83
+ it_behaves_like 'a correct redirect'
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,189 @@
1
+ require 'spec_helper'
2
+ include RedirHelper
3
+
4
+ describe :redir do
5
+
6
+ after :each do
7
+ Redirect.delete_all
8
+ RedirectLog.delete_all
9
+ end
10
+
11
+ shared_examples_for 'code that redirects' do
12
+
13
+ before do
14
+ before
15
+ get redirect.shortened_path.to_s
16
+ end
17
+
18
+ context 'redirects' do
19
+ before do
20
+ follow_redirect!
21
+ end
22
+
23
+ context 'url' do
24
+ subject {last_request}
25
+ its(:url) {should eq redirect.original_url}
26
+ end
27
+
28
+ context 'response' do
29
+ subject {last_response.errors.size}
30
+ it {should eq 0}
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ let(:before) {Time.now}
37
+ let(:after) {Time.now}
38
+
39
+ context 'should apply class level settings for the url and path' do
40
+ before do
41
+ SecureRandom.should_receive(:urlsafe_base64).with(6).and_return('eeeeee')
42
+ Redirect.path = 'foo/'
43
+ Redirect.host = 'http://myredir.com/'
44
+ end
45
+
46
+ let(:redirect) { Redirect.create_shortened_url 'http://www.google.com/' }
47
+ subject {redirect}
48
+ its (:shortened_id) {should eq 'eeeeee'}
49
+ its (:shortened_path) {should eq 'foo/eeeeee'}
50
+ its (:shortened_url) {should eq 'http://myredir.com/foo/eeeeee'}
51
+
52
+ context 'non default path' do
53
+ it_behaves_like 'code that redirects'
54
+ end
55
+
56
+ after do
57
+ Redirect.path = 'a/'
58
+ Redirect.host = 'http://localhost:3000/'
59
+ end
60
+ end
61
+
62
+
63
+ context 'general redirect' do
64
+
65
+ let(:redirect) {create :redirect}
66
+ before do
67
+ before
68
+ get redirect.shortened_path.to_s
69
+ end
70
+
71
+ it_behaves_like 'code that redirects'
72
+
73
+ let(:redirect_log) {RedirectLog.where(:redirect_id => redirect.id).to_a.last}
74
+
75
+ context 'headers' do
76
+ subject {redirect_log.headers}
77
+ its (['HTTP_HOST']) {should eq 'example.org'}
78
+ its (['HTTP_COOKIE']) {should eq ''}
79
+ end
80
+
81
+ context 'request time' do
82
+ subject {redirect_log}
83
+ its (:request_time) {should > before}
84
+ its (:request_time) {should < after}
85
+ end
86
+ end
87
+
88
+ context 'redirect with tracking info appended' do
89
+ before do
90
+ Redirect.append_tracking_info = true
91
+ get redirect.shortened_path.to_s
92
+ follow_redirect!
93
+ end
94
+ let (:redirect_log) {Redirector.find_logs(redirect.shortened_id).last}
95
+
96
+ context 'with existing url data' do
97
+ let (:redirect) {create :redirect, :original_url => 'www.google.com/?foo=bar'}
98
+ subject {last_request}
99
+ its (:url) {should eq "http://www.google.com/?foo=bar&redirect_mongo_id=#{redirect_log.redirect_id}" +
100
+ "&redirect_log_mongo_id=#{redirect_log._id}"}
101
+ end
102
+
103
+ context 'without existing url data' do
104
+ let (:redirect) {create :redirect, :original_url => 'www.google.com'}
105
+ subject {last_request}
106
+ its (:url) {should eq "http://www.google.com/?redirect_mongo_id=#{redirect_log.redirect_id}" +
107
+ "&redirect_log_mongo_id=#{redirect_log._id}"}
108
+ end
109
+
110
+ after do
111
+ Redirect.append_tracking_info = false
112
+ end
113
+ end
114
+
115
+ context 'redirect with google analytics info appended' do
116
+ before do
117
+ Redirect.append_google_analytics = true
118
+ get redirect.shortened_path.to_s
119
+ follow_redirect!
120
+ end
121
+ let (:redirect_log) {Redirector.find_logs(redirect.shortened_id).last}
122
+
123
+ context 'with existing url data' do
124
+ let (:redirect) {create :redirect, :original_url => 'www.google.com/?foo=bar'}
125
+ subject {last_request}
126
+ its (:url) {should eq "http://www.google.com/?foo=bar&utm_source=http%3A%2F%2Flocalhost%3A3000%2F" +
127
+ "&utm_campaign=#{redirect_log.redirect_id}__#{redirect_log._id}"}
128
+ end
129
+
130
+ context 'without existing url data' do
131
+ let (:redirect) {create :redirect, :original_url => 'www.google.com'}
132
+ subject {last_request}
133
+ its (:url) {should eq "http://www.google.com/?utm_source=http%3A%2F%2Flocalhost%3A3000%2F" +
134
+ "&utm_campaign=#{redirect_log.redirect_id}__#{redirect_log._id}"}
135
+ end
136
+
137
+ after do
138
+ Redirect.append_google_analytics = false
139
+ end
140
+ end
141
+
142
+ context 'redirect with no preceeding http' do
143
+ before do
144
+ get redirect.shortened_path.to_s
145
+ follow_redirect!
146
+ end
147
+
148
+ context 'url without http' do
149
+ let (:redirect) {create :redirect, :original_url => 'www.google.com'}
150
+ subject {last_request}
151
+ its (:url) {should eq 'http://www.google.com/'}
152
+ end
153
+
154
+ context 'url without http and leading space' do
155
+ let (:redirect) {create :redirect, :original_url => ' www.google.com'}
156
+ subject {last_request}
157
+ its (:url) {should eq 'http://www.google.com/'}
158
+ end
159
+ end
160
+
161
+ context 'bad redirect' do
162
+
163
+ before do
164
+ get "/a/ugh"
165
+ end
166
+
167
+ context 'status' do
168
+ subject {last_response.status}
169
+ it {should eq 404}
170
+ end
171
+
172
+ end
173
+
174
+ context 'non rdr path' do
175
+
176
+ before do
177
+ get "/rddd/ugh"
178
+ end
179
+
180
+ context 'status' do
181
+ subject {last_response.status}
182
+ it {should eq 200}
183
+ end
184
+
185
+ end
186
+
187
+ end
188
+
189
+
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ include RedirHelper
3
+
4
+ describe :redirector do
5
+
6
+ let(:redirect) { Redirect.create_shortened_url 'http://www.google.com/' }
7
+ before {get redirect.shortened_path.to_s}
8
+
9
+ context 'find logs' do
10
+
11
+ subject {Redirector.find_logs(redirect.shortened_id).to_a}
12
+ its(:size) {should eq 1}
13
+
14
+ context 'data' do
15
+ subject {Redirector.find_logs(redirect.shortened_id).to_a.last.headers['HTTP_HOST']}
16
+ it {should eq 'example.org'}
17
+ end
18
+
19
+ context 'multiple requests' do
20
+ before {get redirect.shortened_path.to_s}
21
+ subject {Redirector.find_logs(redirect.shortened_id).to_a}
22
+ its(:size) {should eq 2}
23
+ end
24
+ end
25
+
26
+ context 'number of redirect hits' do
27
+ subject {Redirector.number_of_redirect_hits redirect.shortened_id}
28
+ it {should eq 1}
29
+
30
+ context 'multiple requests' do
31
+ before {get redirect.shortened_path.to_s}
32
+ subject {Redirector.number_of_redirect_hits redirect.shortened_id}
33
+ it {should eq 2}
34
+ end
35
+
36
+ end
37
+
38
+ after :all do
39
+ Redirect.delete_all
40
+ RedirectLog.delete_all
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,213 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redir
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lance Gleason
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongoid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rack-test
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: factory_girl
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: timecop
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: jeweler
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: simplecov
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: yard
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ description: This gem adds functionality to shorten url's and gather data about requests
159
+ going through it.
160
+ email: lgleason@polyglotprogramminginc.com
161
+ executables: []
162
+ extensions: []
163
+ extra_rdoc_files:
164
+ - LICENSE.txt
165
+ - README.md
166
+ files:
167
+ - Gemfile
168
+ - Gemfile.lock
169
+ - LICENSE.txt
170
+ - README.md
171
+ - Rakefile
172
+ - VERSION
173
+ - lib/model/redirect.rb
174
+ - lib/model/redirect_log.rb
175
+ - lib/rack/redir.rb
176
+ - lib/redir.rb
177
+ - lib/redirector.rb
178
+ - redir.gemspec
179
+ - spec/factories/redirect.rb
180
+ - spec/mongoid.yml
181
+ - spec/spec_helper.rb
182
+ - spec/unit/model/redirect_spec.rb
183
+ - spec/unit/rack/redir_spec.rb
184
+ - spec/unit/redirector_spec.rb
185
+ homepage: http://github.com/springbot/redir
186
+ licenses:
187
+ - MIT
188
+ post_install_message:
189
+ rdoc_options: []
190
+ require_paths:
191
+ - lib
192
+ required_ruby_version: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ segments:
199
+ - 0
200
+ hash: 1346305912499164627
201
+ required_rubygems_version: !ruby/object:Gem::Requirement
202
+ none: false
203
+ requirements:
204
+ - - ! '>='
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ requirements: []
208
+ rubyforge_project:
209
+ rubygems_version: 1.8.24
210
+ signing_key:
211
+ specification_version: 3
212
+ summary: A Mongo based url shortener and data gathering gem.
213
+ test_files: []