From f5e015894d3b6338f02f817af17b2e13675a2d43 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 24 Mar 2011 08:45:29 +0100 Subject: [PATCH] import sinatra-decompile --- sinatra-contrib/lib/sinatra/decompile.rb | 61 ++++++++++++++++++++++++ sinatra-contrib/spec/decompile_spec.rb | 42 ++++++++++++++++ sinatra-contrib/spec/spec_helper.rb | 11 +++++ 3 files changed, 114 insertions(+) create mode 100644 sinatra-contrib/lib/sinatra/decompile.rb create mode 100644 sinatra-contrib/spec/decompile_spec.rb create mode 100644 sinatra-contrib/spec/spec_helper.rb diff --git a/sinatra-contrib/lib/sinatra/decompile.rb b/sinatra-contrib/lib/sinatra/decompile.rb new file mode 100644 index 00000000..93c93c9c --- /dev/null +++ b/sinatra-contrib/lib/sinatra/decompile.rb @@ -0,0 +1,61 @@ +require 'sinatra/base' +require 'backports' + +module Sinatra + ## + # Can be used as extension or stand-alone: + # + # Sinatra::Decompile.decompile(...) + module Decompile + extend self + + ## + # Regenerates a string pattern for a given route + # + # Example: + # + # class Sinatra::Application + # routes.each do |verb, list| + # puts "#{verb}:" + # list.each do |data| + # puts "\t" << decompile(data) + # end + # end + # end + # + # Will return the internal Regexp if unable to reconstruct the pattern, + # which likely indicates that a Regexp was used in the first place. + # + # You can also use this to check whether you could actually use a string + # pattern instead of your regexp: + # + # decompile /^/foo$/ # => '/foo' + def decompile(pattern, keys = nil, *) + # Everything in here is basically just the reverse of + # Sinatra::Base#compile + pattern, keys = pattern if pattern.respond_to? :to_ary + keys, str = keys.try(:dup), pattern.inspect + return pattern unless str.start_with? '/' and str.end_with? '/' + str.gsub! /^\/\^?|\$?\/$/, '' + return pattern if str =~ /^[\.\+]/ + str.gsub! /\([^\(]*\)/ do |part| + case part + when '(.*?)' + return pattern if keys.shift != 'splat' + '*' + when '([^\/?#]+)' + return pattern if keys.empty? + ":" << keys.shift + else + return pattern + end + end + str.gsub /(.)([\.\+\(\)\/])/ do + return pattern if $1 != "\\" + $2 + end + end + end + + register Decompile +end diff --git a/sinatra-contrib/spec/decompile_spec.rb b/sinatra-contrib/spec/decompile_spec.rb new file mode 100644 index 00000000..84a54ba7 --- /dev/null +++ b/sinatra-contrib/spec/decompile_spec.rb @@ -0,0 +1,42 @@ +require 'backports' +require_relative 'spec_helper' + +RSpec::Matchers.define :decompile do |path| + match do |app| + @compiled, @keys = app.send :compile, path + @decompiled = app.decompile(@compiled, @keys) + @decompiled.should == path + end + + failure_message_for_should do |app| + values = [app, @compiled, @keys, path, @decompiled].map(&:inspect) + "expected %s to decompile %s with %s to %s, but was %s" % values + end +end + +describe Sinatra::Decompile do + subject { Sinatra::Application } + it { should decompile("") } + it { should decompile("/") } + it { should decompile("/?") } + it { should decompile("/foo") } + it { should decompile("/:name") } + it { should decompile("/:name?") } + it { should decompile("/:foo/:bar") } + it { should decompile("/page/:id/edit") } + it { should decompile("/hello/*") } + it { should decompile("/*/foo/*") } + it { should decompile("*") } + it { should decompile(":name.:format") } + it { should decompile(/./) } + it { should decompile(/f(oo)/) } + it { should decompile(/ba+r/) } + + it 'just returns strings' do + subject.decompile('/foo').should == '/foo' + end + + it 'just decompile simple regexps without keys' do + subject.decompile(%r{/foo}).should == '/foo' + end +end diff --git a/sinatra-contrib/spec/spec_helper.rb b/sinatra-contrib/spec/spec_helper.rb new file mode 100644 index 00000000..13e9ca10 --- /dev/null +++ b/sinatra-contrib/spec/spec_helper.rb @@ -0,0 +1,11 @@ +require 'forwardable' + +module TestHelpers +end + +require 'sinatra/contrib' + +RSpec.configure do |config| + config.expect_with :rspec, :stdlib + config.include Sinatra::TestHelpers +end