Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

add the memory adapter

  • Loading branch information...
commit 796061f76c72f542ecf83884cf601b02228928e6 0 parents
risk danger olson authored
22 LICENSE
... ... @@ -0,0 +1,22 @@
  1 +The MIT License
  2 +
  3 +Copyright (c) Rick Olson
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in
  13 +all copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21 +THE SOFTWARE.
  22 +
135 Rakefile
... ... @@ -0,0 +1,135 @@
  1 +require 'rubygems'
  2 +require 'rake'
  3 +require 'date'
  4 +
  5 +#############################################################################
  6 +#
  7 +# Helper functions
  8 +#
  9 +#############################################################################
  10 +
  11 +def name
  12 + @name ||= Dir['*.gemspec'].first.split('.').first
  13 +end
  14 +
  15 +def version
  16 + line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
  17 + line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
  18 +end
  19 +
  20 +def date
  21 + Date.today.to_s
  22 +end
  23 +
  24 +def rubyforge_project
  25 + name
  26 +end
  27 +
  28 +def gemspec_file
  29 + "#{name}.gemspec"
  30 +end
  31 +
  32 +def gem_file
  33 + "#{name}-#{version}.gem"
  34 +end
  35 +
  36 +def replace_header(head, header_name)
  37 + head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
  38 +end
  39 +
  40 +#############################################################################
  41 +#
  42 +# Standard tasks
  43 +#
  44 +#############################################################################
  45 +
  46 +task :default => :test
  47 +
  48 +require 'rake/testtask'
  49 +Rake::TestTask.new(:test) do |test|
  50 + test.libs << 'lib' << 'test'
  51 + test.pattern = 'test/**/*_test.rb'
  52 + test.verbose = true
  53 +end
  54 +
  55 +desc "Open an irb session preloaded with this library"
  56 +task :console do
  57 + sh "irb -rubygems -r ./lib/#{name}.rb"
  58 +end
  59 +
  60 +#############################################################################
  61 +#
  62 +# Custom tasks (add your own tasks here)
  63 +#
  64 +#############################################################################
  65 +
  66 +
  67 +
  68 +#############################################################################
  69 +#
  70 +# Packaging tasks
  71 +#
  72 +#############################################################################
  73 +
  74 +desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
  75 +task :release => :build do
  76 + unless `git branch` =~ /^\* master$/
  77 + puts "You must be on the master branch to release!"
  78 + exit!
  79 + end
  80 + sh "git commit --allow-empty -a -m 'Release #{version}'"
  81 + sh "git tag v#{version}"
  82 + sh "git push origin master"
  83 + sh "git push origin v#{version}"
  84 + sh "gem push pkg/#{name}-#{version}.gem"
  85 +end
  86 +
  87 +desc "Build #{gem_file} into the pkg directory"
  88 +task :build => :gemspec do
  89 + sh "mkdir -p pkg"
  90 + sh "gem build #{gemspec_file}"
  91 + sh "mv #{gem_file} pkg"
  92 +end
  93 +
  94 +desc "Generate #{gemspec_file}"
  95 +task :gemspec => :validate do
  96 + # read spec file and split out manifest section
  97 + spec = File.read(gemspec_file)
  98 + head, manifest, tail = spec.split(" # = MANIFEST =\n")
  99 +
  100 + # replace name version and date
  101 + replace_header(head, :name)
  102 + replace_header(head, :version)
  103 + replace_header(head, :date)
  104 + #comment this out if your rubyforge_project has a different name
  105 + replace_header(head, :rubyforge_project)
  106 +
  107 + # determine file list from git ls-files
  108 + files = `git ls-files`.
  109 + split("\n").
  110 + sort.
  111 + reject { |file| file =~ /^\./ }.
  112 + reject { |file| file =~ /^(rdoc|pkg)/ }.
  113 + map { |file| " #{file}" }.
  114 + join("\n")
  115 +
  116 + # piece file back together and write
  117 + manifest = " s.files = %w[\n#{files}\n ]\n"
  118 + spec = [head, manifest, tail].join(" # = MANIFEST =\n")
  119 + File.open(gemspec_file, 'w') { |io| io.write(spec) }
  120 + puts "Updated #{gemspec_file}"
  121 +end
  122 +
  123 +desc "Validate #{gemspec_file}"
  124 +task :validate do
  125 + libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
  126 + unless libfiles.empty?
  127 + puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
  128 + exit!
  129 + end
  130 + unless Dir['VERSION*'].empty?
  131 + puts "A `VERSION` file at root level violates Gem best practices."
  132 + exit!
  133 + end
  134 +end
  135 +
55 gitio.gemspec
... ... @@ -0,0 +1,55 @@
  1 +## This is the rakegem gemspec template. Make sure you read and understand
  2 +## all of the comments. Some sections require modification, and others can
  3 +## be deleted if you don't need them. Once you understand the contents of
  4 +## this file, feel free to delete any comments that begin with two hash marks.
  5 +## You can find comprehensive Gem::Specification documentation, at
  6 +## http://docs.rubygems.org/read/chapter/20
  7 +Gem::Specification.new do |s|
  8 + s.specification_version = 2 if s.respond_to? :specification_version=
  9 + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
  10 + s.rubygems_version = '1.3.5'
  11 +
  12 + ## Leave these as is they will be modified for you by the rake gemspec task.
  13 + ## If your rubyforge_project name is different, then edit it and comment out
  14 + ## the sub! line in the Rakefile
  15 + s.name = 'NAME'
  16 + s.version = '0.0'
  17 + s.date = '2010-01-01'
  18 + s.rubyforge_project = 'NAME'
  19 +
  20 + ## Make sure your summary is short. The description may be as long
  21 + ## as you like.
  22 + s.summary = "Adaptable private URL shortener"
  23 + s.description = "Adaptable private URL shortener"
  24 +
  25 + ## List the primary authors. If there are a bunch of authors, it's probably
  26 + ## better to set the email to an email list or something. If you don't have
  27 + ## a custom homepage, consider using your GitHub URL or the like.
  28 + s.authors = ["Rick Olson"]
  29 + s.email = 'technoweenie@gmail.com'
  30 + s.homepage = 'https://github.com/technoweenie/gitio'
  31 +
  32 + ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
  33 + ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
  34 + s.require_paths = %w[lib]
  35 +
  36 + ## List your runtime dependencies here. Runtime dependencies are those
  37 + ## that are needed for an end user to actually USE your code.
  38 + #s.add_dependency('DEPNAME', [">= 1.1.0", "< 2.0.0"])
  39 +
  40 + ## List your development dependencies here. Development dependencies are
  41 + ## those that are only needed during development
  42 + #s.add_development_dependency('DEVDEPNAME', [">= 1.1.0", "< 2.0.0"])
  43 +
  44 + ## Leave this section as-is. It will be automatically generated from the
  45 + ## contents of your Git repository via the gemspec task. DO NOT REMOVE
  46 + ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
  47 + # = MANIFEST =
  48 + s.files = %w[]
  49 + # = MANIFEST =
  50 +
  51 + ## Test files will be grabbed from the file list. Make sure the path glob
  52 + ## matches what you actually use.
  53 + s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
  54 +end
  55 +
30 lib/gitio.rb
... ... @@ -0,0 +1,30 @@
  1 +require 'base64'
  2 +require 'digest/md5'
  3 +
  4 +module Gitio
  5 + VERSION = "0.0.1"
  6 +
  7 + module Adapters
  8 + dir = File.expand_path '../gitio/adapters', __FILE__
  9 + autoload :MemoryAdapter, "#{dir}/memory_adapter"
  10 +
  11 + # Adapters handle the storage and retrieval of URLs in the system. You can
  12 + # use whatever you want, as long as it implements the #add and #find
  13 + # methods. See MemoryAdapter for a simple solution.
  14 + class Adapter
  15 + # Public: Shortens a given URL to a short code.
  16 + #
  17 + # 1) MD5 hash the URL to the hexdigest
  18 + # 2) Convert it to a Bignum
  19 + # 3) Pack it into a bitstring as a big-endian int
  20 + # 4) base64-encode the bitstring, remove the trailing junk
  21 + #
  22 + # url - String URL to shorten.
  23 + #
  24 + # Returns a unique String code for the URL.
  25 + def shorten(url)
  26 + Base64.urlsafe_encode64([Digest::MD5.hexdigest(url).to_i(16)].pack("N")).sub(/==\n?$/, '')
  27 + end
  28 + end
  29 + end
  30 +end
37 lib/gitio/adapters/memory_adapter.rb
... ... @@ -0,0 +1,37 @@
  1 +module Gitio
  2 + module Adapters
  3 + # Stores shortened URLs in memory. Totally scales.
  4 + class MemoryAdapter < Adapter
  5 + def initialize
  6 + @hash = {}
  7 + @urls = {}
  8 + end
  9 +
  10 + # Public: Stores the shortened version of a URL.
  11 + #
  12 + # url - The String URL to shorten and store.
  13 + #
  14 + # Returns the unique String code for the URL. If the URL is added
  15 + # multiple times, this should return the same code.
  16 + def add(url)
  17 + if code = @urls[url]
  18 + code
  19 + else
  20 + code = shorten(url)
  21 + @hash[code] = url
  22 + @urls[url] = code
  23 + code
  24 + end
  25 + end
  26 +
  27 + # Public: Retrieves a URL from the code.
  28 + #
  29 + # code - The String code to lookup the URL.
  30 + #
  31 + # Returns the String URL.
  32 + def find(code)
  33 + @hash[code]
  34 + end
  35 + end
  36 + end
  37 +end
5 test/helper.rb
... ... @@ -0,0 +1,5 @@
  1 +require 'test/unit'
  2 +require File.expand_path('../../lib/gitio', __FILE__)
  3 +
  4 +class Gitio::TestCase < Test::Unit::TestCase
  5 +end
17 test/memory_adapter_test.rb
... ... @@ -0,0 +1,17 @@
  1 +require File.expand_path('../helper', __FILE__)
  2 +
  3 +class MemoryAdapterTest < Test::Unit::TestCase
  4 + def setup
  5 + @db = Gitio::Adapters::MemoryAdapter.new
  6 + end
  7 +
  8 + def test_adding_a_link_returns_code
  9 + code = @db.add 'abc'
  10 + assert_equal 'abc', @db.find(code)
  11 + end
  12 +
  13 + def test_adding_duplicate_link_returns_same_code
  14 + code = @db.add 'abc'
  15 + assert_equal code, @db.add('abc')
  16 + end
  17 +end

0 comments on commit 796061f

Please sign in to comment.
Something went wrong with that request. Please try again.