shortener 0.0.1

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.
Files changed (51) hide show
  1. data/.gitignore +34 -0
  2. data/Gemfile +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +35 -0
  5. data/Rakefile +29 -0
  6. data/app/controllers/shortener/shortened_urls_controller.rb +27 -0
  7. data/app/helpers/shortener/shortener_helper.rb +24 -0
  8. data/app/models/shortener/shortened_url.rb +80 -0
  9. data/config/routes.rb +3 -0
  10. data/lib/generators/shortener/shortener_generator.rb +21 -0
  11. data/lib/generators/shortener/templates/migration.rb +22 -0
  12. data/lib/shortener.rb +17 -0
  13. data/lib/shortener/engine.rb +10 -0
  14. data/lib/shortener/version.rb +3 -0
  15. data/shortener.gemspec +21 -0
  16. data/test/dummy/Rakefile +7 -0
  17. data/test/dummy/app/controllers/application_controller.rb +3 -0
  18. data/test/dummy/app/helpers/application_helper.rb +2 -0
  19. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  20. data/test/dummy/config.ru +4 -0
  21. data/test/dummy/config/application.rb +45 -0
  22. data/test/dummy/config/boot.rb +10 -0
  23. data/test/dummy/config/database.yml +22 -0
  24. data/test/dummy/config/environment.rb +5 -0
  25. data/test/dummy/config/environments/development.rb +26 -0
  26. data/test/dummy/config/environments/production.rb +49 -0
  27. data/test/dummy/config/environments/test.rb +35 -0
  28. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  29. data/test/dummy/config/initializers/inflections.rb +10 -0
  30. data/test/dummy/config/initializers/mime_types.rb +5 -0
  31. data/test/dummy/config/initializers/secret_token.rb +7 -0
  32. data/test/dummy/config/initializers/session_store.rb +8 -0
  33. data/test/dummy/config/locales/en.yml +5 -0
  34. data/test/dummy/config/routes.rb +58 -0
  35. data/test/dummy/public/404.html +26 -0
  36. data/test/dummy/public/422.html +26 -0
  37. data/test/dummy/public/500.html +26 -0
  38. data/test/dummy/public/favicon.ico +0 -0
  39. data/test/dummy/public/javascripts/application.js +2 -0
  40. data/test/dummy/public/javascripts/controls.js +965 -0
  41. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  42. data/test/dummy/public/javascripts/effects.js +1123 -0
  43. data/test/dummy/public/javascripts/prototype.js +6001 -0
  44. data/test/dummy/public/javascripts/rails.js +191 -0
  45. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  46. data/test/dummy/script/rails +6 -0
  47. data/test/integration/navigation_test.rb +7 -0
  48. data/test/shortener_test.rb +7 -0
  49. data/test/support/integration_case.rb +5 -0
  50. data/test/test_helper.rb +22 -0
  51. metadata +117 -0
@@ -0,0 +1,34 @@
1
+ .DS_Store
2
+ log/*
3
+ tmp/**/*
4
+ bin/*
5
+
6
+ config/database.yml
7
+ db/*.sqlite3
8
+ *~
9
+ public/photos/*
10
+ \#*\#
11
+ #Ignore all log files and process ID files
12
+ *.log
13
+ *.pid
14
+
15
+ #ignore all generated pshinx config files
16
+ *.sphinx.conf
17
+
18
+ #ignore all sphinx DB files
19
+ *.spa
20
+ *.spd
21
+ *.sph
22
+ *.spi
23
+ *.spk
24
+ *.spl
25
+ *.spm
26
+ *.spp
27
+
28
+ #ignore radrails files and temp files
29
+ .project
30
+ .loadpath
31
+ ._*
32
+
33
+ .bundle
34
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify any dependencies in the gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright 2011 James P. McGrath
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.
@@ -0,0 +1,35 @@
1
+ = Shortener
2
+
3
+ Shortener makes it easy to create shortened URLs for your rails application.
4
+
5
+ == Installation
6
+
7
+ You can use the latest Rails 3 gem with the latest Shortener gem. In your Gemfile:
8
+
9
+ gem 'shortener'
10
+
11
+ After you install Shortener run the generator:
12
+
13
+ rails generate shortener
14
+
15
+ This generator will create a migration to create the shortened_urls table where your shortened URLs will be stored.
16
+
17
+ == Usage
18
+
19
+ To generate a Shortened URL object for the URL "http://dealush.com" within your controller / models do the following:
20
+
21
+ Shortener::ShortenedURL.generate("http://dealush.com")
22
+
23
+ or
24
+
25
+ Shortener::ShortenedURL.generate("dealush.com")
26
+
27
+ To generate and display a shortened URL in your application use the helper method:
28
+
29
+ shortened_url("dealush.com")
30
+
31
+ This will generate a shortened URL. store it to the db and return a string representing the shortened URL.
32
+
33
+ == Notes
34
+
35
+ This is the first release and still has some bugs. I will be releasing fixes for these bugs soon.
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rake/rdoctask'
11
+
12
+ require 'rake/testtask'
13
+
14
+ Rake::TestTask.new(:test) do |t|
15
+ t.libs << 'lib'
16
+ t.libs << 'test'
17
+ t.pattern = 'test/**/*_test.rb'
18
+ t.verbose = false
19
+ end
20
+
21
+ task :default => :test
22
+
23
+ Rake::RDocTask.new(:rdoc) do |rdoc|
24
+ rdoc.rdoc_dir = 'rdoc'
25
+ rdoc.title = 'Shortener'
26
+ rdoc.options << '--line-numbers' << '--inline-source'
27
+ rdoc.rdoc_files.include('README.rdoc')
28
+ rdoc.rdoc_files.include('lib/**/*.rb')
29
+ end
@@ -0,0 +1,27 @@
1
+ module Shortener
2
+ class ShortenedUrlsController < ::ApplicationController
3
+
4
+ # find the real link for the shortened link key and redirect
5
+ def translate
6
+ # pull the link out of the db
7
+ sl = ShortenedUrl.find_by_unique_key(params[:unique_key])
8
+
9
+ if sl
10
+ # don't want to wait for the increment to happen, make it snappy!
11
+ # this is the place to enhance the metrics captured
12
+ # for the system. You could log the request origin
13
+ # browser type, ip address etc.
14
+ Thread.new do
15
+ sl.increment!(:use_count)
16
+ end
17
+ # do a 301 redirect to the destination url
18
+ head :moved_permanently, :location => sl.url
19
+ else
20
+ # if we don't find the shortened link, redirect to the root
21
+ # make this configurable in future versions
22
+ head :moved_permanently, :location => root_url
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ module Shortener::ShortenerHelper
2
+
3
+ # generate a url from either a url string, or a shortened url object
4
+ def shortened_url(url_object, user=nil)
5
+
6
+ short_url = nil
7
+
8
+ if url_object.class != String #== ShortenedUrl
9
+ if user.nil?
10
+ short_url = url_object
11
+ else
12
+ # if the user has passed in a shortened url, with a user, then
13
+ # work out the link for the shortened url and make another with the
14
+ # passed user
15
+ short_url = ShortenedUrl.generate(shortened_url(url_object), user)
16
+ end
17
+ else
18
+ short_url = ShortenedUrl.generate(url_object, user)
19
+ end
20
+
21
+ return short_url.nil? ? nil : shortener_translate_url(short_url.unique_key)
22
+ end
23
+
24
+ end
@@ -0,0 +1,80 @@
1
+ module Shortener
2
+ class ShortenedUrl < ActiveRecord::Base
3
+
4
+ UNIQUE_KEY_LENGTH = 5
5
+ URL_PROTOCOL_HTTP = "http://"
6
+
7
+ REGEX_HTTP_URL = /^\s*(http[s]?:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\s*$/i
8
+ REGEX_LINK_HAS_PROTOCOL = Regexp.new('\Ahttp:\/\/|\Ahttps:\/\/', Regexp::IGNORECASE)
9
+ REGEX_EMAIL = /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
10
+
11
+ validates_format_of :url, :with => REGEX_HTTP_URL, :allow_blank => true
12
+ validates_presence_of :url
13
+ validates_uniqueness_of :unique_key
14
+
15
+ belongs_to :user # allows the shortened link to be associated with a user
16
+
17
+ before_validation :clean_destination_url, :init_unique_key, :on => :create
18
+
19
+
20
+ # ensure the url starts with it protocol
21
+ def clean_destination_url
22
+ if !self.url.blank? and self.url !~ REGEX_LINK_HAS_PROTOCOL
23
+ self.url.insert(0, URL_PROTOCOL_HTTP)
24
+ end
25
+ end
26
+
27
+ def init_unique_key
28
+ # generate a unique key for the link
29
+ begin
30
+ # has about 50 million possible combos
31
+ self.unique_key = ShortenedUrl::generate_unique_key
32
+ end while ShortenedUrl::find_by_unique_key self.unique_key
33
+ end
34
+
35
+ # generate a shortened link from a url
36
+ # link to a user if one specified
37
+ # throw an exception if anything goes wrong
38
+ def self.generate!(orig_url, user=nil)
39
+ # don't want to generate the link if it has already been generated
40
+ # so check the datastore
41
+ uid = user.nil? ? nil : user.id
42
+ sl = ShortenedUrl.find_by_url_and_user_id(orig_url, uid)
43
+
44
+ return sl if sl
45
+
46
+ # create the shortened link, storing it
47
+ sl = ShortenedUrl.create!(:url => orig_url, :user => user)
48
+
49
+ # return the url
50
+ return sl
51
+ end
52
+
53
+ # return shortened url on success, nil on failure
54
+ def self.generate(orig_url, user=nil)
55
+
56
+ sl = nil
57
+
58
+ begin
59
+ sl = ShortenedUrl::generate!(orig_url, user)
60
+ rescue
61
+ sl = nil
62
+ end
63
+
64
+ return sl
65
+ end
66
+
67
+
68
+
69
+ private
70
+
71
+ # generate a random string
72
+ # future mod to allow specifying a more expansive charst, like utf-8 chinese
73
+ def self.generate_unique_key(size = UNIQUE_KEY_LENGTH)
74
+ # not doing uppercase as url is case insensitive
75
+ charset = ('a'..'z').to_a + (0..9).to_a
76
+ (0...size).map{ charset.to_a[rand(charset.size)] }.join
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ get '/s/:unique_key', :to => 'shortener/shortened_urls#translate', :as => 'shortener_translate'
3
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class ShortenerGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+ def self.source_root
7
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
8
+ end
9
+
10
+ def self.next_migration_number(dirname)
11
+ if ActiveRecord::Base.timestamped_migrations
12
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
13
+ else
14
+ "%.3d" % (current_migration_number(dirname) + 1)
15
+ end
16
+ end
17
+
18
+ def create_migration_file
19
+ migration_template 'migration.rb', 'db/migrate/create_shortened_urls_table.rb'
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ class CreateShortenedUrlsTable < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :shortened_urls do |t|
4
+
5
+ t.integer :user_id # we can link this to a user for interesting things
6
+ t.string :url, :null => false # the real url that we will redirect to
7
+ t.string :unique_key, :null => false # the unique key
8
+ t.integer :use_count, :null => false, :default => 0 # how many times the link has been clicked
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :shortened_urls, :unique_key # we will lookup the links in the db with this
14
+ add_index :shortened_urls, :user_id # and this
15
+ end
16
+
17
+ def self.down
18
+ remove_index :shortened_urls, :unique_key
19
+ remove_index :shortened_urls, :user_id
20
+ drop_table :shortened_urls
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require "active_support/dependencies"
2
+
3
+ module Shortener
4
+
5
+ # Our host application root path
6
+ # We set this when the engine is initialized
7
+ mattr_accessor :app_root
8
+
9
+ # Yield self on setup for nice config blocks
10
+ def self.setup
11
+ yield self
12
+ end
13
+
14
+ end
15
+
16
+ # Require our engine
17
+ require "shortener/engine"
@@ -0,0 +1,10 @@
1
+ require "rails/engine"
2
+ require "shortener"
3
+
4
+ module Shortener
5
+
6
+ class ShortenerEngine < Rails::Engine
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,3 @@
1
+ module Shortener
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path("../lib/shortener/version", __FILE__)
2
+
3
+ # Provide a simple gemspec so you can easily use your enginex
4
+ # project in your rails apps through git.
5
+ Gem::Specification.new do |s|
6
+ s.name = "shortener"
7
+ s.summary = "Shortener makes it easy to create shortened URLs for your rails application."
8
+ s.description = "Shortener makes it easy to create shortened URLs for your rails application."
9
+ s.files = `git ls-files`.split("\n")
10
+ s.version = Shortener::VERSION
11
+ s.platform = Gem::Platform::RUBY
12
+ s.authors = [ "James P. McGrath" ]
13
+ s.email = [ "gems@jamespmcgrath.com" ]
14
+ s.homepage = "http://jamespmcgrath.com/projects/shortener"
15
+ s.rubyforge_project = "shortener"
16
+ s.required_rubygems_version = "> 1.3.6"
17
+ s.add_dependency "activesupport" , ">= 3.0.7"
18
+ s.add_dependency "rails" , ">= 3.0.7"
19
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
20
+ s.require_path = 'lib'
21
+ end
@@ -0,0 +1,7 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+ require 'rake'
6
+
7
+ Dummy::Application.load_tasks
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= stylesheet_link_tag :all %>
6
+ <%= javascript_include_tag :defaults %>
7
+ <%= csrf_meta_tag %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Dummy::Application
@@ -0,0 +1,45 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require "active_model/railtie"
4
+ require "active_record/railtie"
5
+ require "action_controller/railtie"
6
+ require "action_view/railtie"
7
+ require "action_mailer/railtie"
8
+
9
+ Bundler.require
10
+ require "shortener"
11
+
12
+ module Dummy
13
+ class Application < Rails::Application
14
+ # Settings in config/environments/* take precedence over those specified here.
15
+ # Application configuration should go into files in config/initializers
16
+ # -- all .rb files in that directory are automatically loaded.
17
+
18
+ # Custom directories with classes and modules you want to be autoloadable.
19
+ # config.autoload_paths += %W(#{config.root}/extras)
20
+
21
+ # Only load the plugins named here, in the order given (default is alphabetical).
22
+ # :all can be used as a placeholder for all plugins not explicitly named.
23
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24
+
25
+ # Activate observers that should always be running.
26
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
27
+
28
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30
+ # config.time_zone = 'Central Time (US & Canada)'
31
+
32
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34
+ # config.i18n.default_locale = :de
35
+
36
+ # JavaScript files you want as :defaults (application.js is always included).
37
+ # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
38
+
39
+ # Configure the default encoding used in templates for Ruby 1.9.
40
+ config.encoding = "utf-8"
41
+
42
+ # Configure sensitive parameters which will be filtered from the log file.
43
+ config.filter_parameters += [:password]
44
+ end
45
+ end