usher 0.5.11 → 0.5.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/Rakefile +48 -53
  2. data/VERSION.yml +2 -2
  3. data/lib/usher.rb +12 -12
  4. data/lib/usher/delimiters.rb +7 -7
  5. data/lib/usher/interface.rb +19 -30
  6. data/lib/usher/interface/{email_interface.rb → email.rb} +1 -1
  7. data/lib/usher/interface/{merb_interface.rb → merb.rb} +1 -1
  8. data/lib/usher/interface/{rack_interface.rb → rack.rb} +14 -10
  9. data/lib/usher/interface/{rack_interface → rack}/mapper.rb +0 -0
  10. data/lib/usher/interface/rack/route.rb +16 -0
  11. data/lib/usher/interface/rails20.rb +7 -0
  12. data/lib/usher/interface/{rails2_2_interface.rb → rails22.rb} +18 -18
  13. data/lib/usher/interface/{rails2_2_interface → rails22}/mapper.rb +2 -2
  14. data/lib/usher/interface/{rails2_3_interface.rb → rails23.rb} +1 -1
  15. data/lib/usher/interface/rails23/mapper.rb +44 -0
  16. data/lib/usher/interface/{rails3_interface.rb → rails3.rb} +1 -1
  17. data/lib/usher/interface/{text_interface.rb → text.rb} +1 -1
  18. data/lib/usher/node.rb +71 -57
  19. data/lib/usher/route.rb +10 -5
  20. data/lib/usher/route/static.rb +9 -0
  21. data/lib/usher/route/variable.rb +28 -9
  22. data/lib/usher/util.rb +3 -3
  23. data/lib/usher/util/generate.rb +55 -18
  24. data/lib/usher/util/parser.rb +25 -18
  25. data/rails/init.rb +24 -8
  26. data/spec/private/generate_spec.rb +116 -1
  27. data/spec/private/generate_with_spec.rb +28 -0
  28. data/spec/private/rack/dispatch_spec.rb +23 -2
  29. data/spec/private/rack/route_spec.rb +50 -0
  30. data/spec/private/rails2_2/generate_spec.rb +1 -1
  31. data/spec/private/rails2_2/path_spec.rb +1 -1
  32. data/spec/private/rails2_2/recognize_spec.rb +1 -1
  33. data/spec/private/rails2_3/generate_spec.rb +1 -1
  34. data/spec/private/rails2_3/path_spec.rb +1 -1
  35. data/spec/private/rails2_3/recognize_spec.rb +1 -1
  36. data/spec/private/recognize_spec.rb +4 -5
  37. data/spec/private/url_parts_spec.rb +116 -0
  38. metadata +26 -12
  39. data/lib/usher/interface/rack_interface/route.rb +0 -16
data/Rakefile CHANGED
@@ -1,53 +1,48 @@
1
- libdir = File.expand_path("lib")
2
- $:.unshift(libdir) unless $:.include?(libdir)
3
-
4
- require 'usher'
5
-
6
- begin
7
- require 'jeweler'
8
- Jeweler::Tasks.new do |s|
9
- s.name = "usher"
10
- s.description = s.summary = "A general purpose routing library"
11
- s.email = "joshbuddy@gmail.com"
12
- s.homepage = "http://github.com/joshbuddy/usher"
13
- s.authors = ["Joshua Hull"]
14
- s.files = FileList["[A-Z]*", "{lib,spec,rails}/**/*"]
15
- s.add_dependency 'fuzzyhash', '>=0.0.9'
16
- s.rubyforge_project = 'joshbuddy-usher'
17
- end
18
- Jeweler::GemcutterTasks.new
19
- Jeweler::RubyforgeTasks.new do |rubyforge|
20
- rubyforge.doc_task = "rdoc"
21
- rubyforge.remote_doc_path = ''
22
- end
23
- rescue LoadError
24
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
25
- end
26
-
27
- require 'spec'
28
- require 'spec/rake/spectask'
29
- task :spec => 'spec:all'
30
- namespace(:spec) do
31
- Spec::Rake::SpecTask.new(:all) do |t|
32
- t.spec_opts ||= []
33
- t.spec_opts << "-rubygems"
34
- t.spec_opts << "--options" << "spec/spec.opts"
35
- t.spec_files = FileList['spec/**/*_spec.rb']
36
- end
37
-
38
- end
39
-
40
- desc "Run all examples with RCov"
41
- Spec::Rake::SpecTask.new('spec_with_rcov') do |t|
42
- t.spec_files = FileList['spec/**/*.rb']
43
- t.rcov = true
44
- t.rcov_opts = ['--exclude', 'spec']
45
- end
46
-
47
- require 'rake/rdoctask'
48
- desc "Generate documentation"
49
- Rake::RDocTask.new do |rd|
50
- rd.main = "README.rdoc"
51
- rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
52
- rd.rdoc_dir = 'rdoc'
53
- end
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'usher'))
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |s|
8
+ s.name = "usher"
9
+ s.description = s.summary = "A general purpose routing library"
10
+ s.email = "joshbuddy@gmail.com"
11
+ s.homepage = "http://github.com/joshbuddy/usher"
12
+ s.authors = ["Joshua Hull", 'Jakub Šťastný', 'Daniel Neighman', 'Daniel Vartanov'].sort
13
+ s.files = FileList["[A-Z]*", "{lib,spec,rails}/**/*"]
14
+ s.add_dependency 'fuzzyhash', '>=0.0.9'
15
+ s.rubyforge_project = 'joshbuddy-usher'
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
20
+ end
21
+
22
+ require 'spec'
23
+ require 'spec/rake/spectask'
24
+ task :spec => 'spec:all'
25
+ namespace(:spec) do
26
+ Spec::Rake::SpecTask.new(:all) do |t|
27
+ t.spec_opts ||= []
28
+ t.spec_opts << "-rubygems"
29
+ t.spec_opts << "--options" << "spec/spec.opts"
30
+ t.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ end
34
+
35
+ desc "Run all examples with RCov"
36
+ Spec::Rake::SpecTask.new('spec_with_rcov') do |t|
37
+ t.spec_files = FileList['spec/**/*.rb']
38
+ t.rcov = true
39
+ t.rcov_opts = ['--exclude', 'spec']
40
+ end
41
+
42
+ require 'rake/rdoctask'
43
+ desc "Generate documentation"
44
+ Rake::RDocTask.new do |rd|
45
+ rd.main = "README.rdoc"
46
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
47
+ rd.rdoc_dir = 'rdoc'
48
+ end
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :build:
3
- :patch: 11
4
- :major: 0
5
3
  :minor: 5
4
+ :patch: 12
5
+ :major: 0
@@ -1,12 +1,13 @@
1
- require File.join(File.dirname(__FILE__), 'usher', 'node')
2
- require File.join(File.dirname(__FILE__), 'usher', 'route')
3
- require File.join(File.dirname(__FILE__), 'usher', 'grapher')
4
- require File.join(File.dirname(__FILE__), 'usher', 'interface')
5
- require File.join(File.dirname(__FILE__), 'usher', 'splitter')
6
- require File.join(File.dirname(__FILE__), 'usher', 'exceptions')
7
- require File.join(File.dirname(__FILE__), 'usher', 'util')
8
- require File.join(File.dirname(__FILE__), 'usher', 'spinoffs', 'strscan_additions')
9
- require File.join(File.dirname(__FILE__), 'usher', 'delimiters')
1
+ $: << File.expand_path(File.dirname(__FILE__))
2
+ require File.join('usher', 'node')
3
+ require File.join('usher', 'route')
4
+ require File.join('usher', 'grapher')
5
+ require File.join('usher', 'interface')
6
+ require File.join('usher', 'splitter')
7
+ require File.join('usher', 'exceptions')
8
+ require File.join('usher', 'util')
9
+ require File.join('usher', 'spinoffs', 'strscan_additions')
10
+ require File.join('usher', 'delimiters')
10
11
 
11
12
  class Usher
12
13
  attr_reader :root, :named_routes, :routes, :splitter,
@@ -53,8 +54,7 @@ class Usher
53
54
  # Array of methods called against the request object for the purposes of matching route requirements.
54
55
  def initialize(options = nil)
55
56
  self.generator = options && options.delete(:generator)
56
- delimiters_array = options && options.delete(:delimiters) || ['/', '.']
57
- self.delimiters = Delimiters.new(delimiters_array)
57
+ self.delimiters = Delimiters.new(options && options.delete(:delimiters) || ['/', '.'])
58
58
  self.valid_regex = options && options.delete(:valid_regex) || '[0-9A-Za-z\$\-_\+!\*\',]+'
59
59
  self.request_methods = options && options.delete(:request_methods)
60
60
  reset!
@@ -95,7 +95,7 @@ class Usher
95
95
  # route = set.add_route('/test')
96
96
  # set.name(:test, route)
97
97
  def name(name, route)
98
- @named_routes[name] = route
98
+ @named_routes[name.to_sym] = route
99
99
  route
100
100
  end
101
101
 
@@ -1,14 +1,14 @@
1
1
  class Delimiters < Array
2
- # TODO: caching
2
+
3
+ attr_reader :unescaped
3
4
 
4
- def unescaped
5
- self.map do |delimiter|
6
- (delimiter[0] == ?\\) ?
7
- delimiter[1..-1] :
8
- delimiter
5
+ def initialize(ary)
6
+ super ary
7
+ @unescaped = self.map do |delimiter|
8
+ (delimiter[0] == ?\\) ? delimiter[1..-1] : delimiter
9
9
  end
10
10
  end
11
-
11
+
12
12
  def first_in(array)
13
13
  # TODO: should we optimize this O(n*m)? hash or modified or KNP or at leaset sort + b-search. But they are so short
14
14
 
@@ -1,43 +1,32 @@
1
- # From Extlib
2
- module CamelCaseMixin
3
- def camel_case
4
- return self if self !~ /_/ && self =~ /[A-Z]+.*/
5
- split('_').map{|e| e.capitalize}.join
6
- end
7
- end
8
-
9
- # TODO: refactoring: I suggest to use usher/interfaces/rack.rb instead of
10
- # usher/interface/rack_interface.rb, it will enable me to simplify this code
11
1
  class Usher
12
2
  module Interface
13
- # Get root directory of interfaces of path to specified interface
14
- def self.interface_directory
15
- File.join(File.dirname(__FILE__), "interface")
16
- end
17
3
 
18
- # path to file
19
- def self.interface_path(name)
20
- File.join(self.interface_directory, "#{name}_interface.rb")
4
+ InterfaceRegistry = {}
5
+
6
+ def self.register(name, cls)
7
+ InterfaceRegistry[name] = cls
21
8
  end
22
9
 
10
+ register(:email, File.join(File.dirname(__FILE__), 'interface', 'email'))
11
+ register(:merb, File.join(File.dirname(__FILE__), 'interface', 'merb'))
12
+ register(:rails20, File.join(File.dirname(__FILE__), 'interface', 'rails20'))
13
+ register(:rails22, File.join(File.dirname(__FILE__), 'interface', 'rails22'))
14
+ register(:rails23, File.join(File.dirname(__FILE__), 'interface', 'rails23'))
15
+ register(:rack, File.join(File.dirname(__FILE__), 'interface', 'rack'))
16
+ register(:rails3, File.join(File.dirname(__FILE__), 'interface', 'rails3'))
17
+ register(:text, File.join(File.dirname(__FILE__), 'interface', 'text'))
18
+
23
19
  # Usher::Interface.for(:rack, &block)
24
20
  def self.for(name, &block)
25
- if File.exist?(self.interface_path(name))
26
- require self.interface_path(name)
27
- snake_cased = "#{name}_interface".extend(CamelCaseMixin)
28
- Usher::Interface.const_get(snake_cased.camel_case).new(&block)
21
+ name = name.to_sym
22
+ if InterfaceRegistry[name]
23
+ require InterfaceRegistry[name]
24
+ const = Usher::Interface.const_get(File.basename(InterfaceRegistry[name]).to_s.split(/_/).map{|e| e.capitalize}.join)
25
+ const.new(&block)
29
26
  else
30
- raise ArgumentError, "Interface #{name} doesn't exist. Choose one of: #{self.interfaces.inspect}"
27
+ raise ArgumentError, "Interface #{name.inspect} doesn't exist. Choose one of: #{InterfaceRegistry.keys.inspect}"
31
28
  end
32
29
  end
33
30
 
34
- # Array of symbols
35
- # Usher::Interface.interfaces
36
- # => [:email_interface, :merb_interface, :rack_interface, :rails2_2_interface, :rails2_3_interface, :rails3_interface, :text_interface]
37
- def self.interfaces
38
- Dir["#{self.interface_directory}/*.rb"].map do |interface|
39
- File.basename(interface).sub("_interface.rb", "").to_sym
40
- end
41
- end
42
31
  end
43
32
  end
@@ -1,6 +1,6 @@
1
1
  class Usher
2
2
  module Interface
3
- class EmailInterface
3
+ class Email
4
4
 
5
5
  def initialize(&blk)
6
6
  @routes = Usher.new(:delimiters => ['@', '-', '.'], :valid_regex => '[\+a-zA-Z0-9]+')
@@ -3,7 +3,7 @@ require 'merb-core/dispatch/router/behavior'
3
3
 
4
4
  class Usher
5
5
  module Interface
6
- class MerbInterface
6
+ class Merb
7
7
 
8
8
  # merb does everything with class methods.
9
9
 
@@ -1,11 +1,12 @@
1
- require 'rack'
1
+ require "rack"
2
+ require File.join(File.dirname(__FILE__), 'rack', 'route')
2
3
 
3
4
  class Usher
4
5
  module Interface
5
- class RackInterface
6
- class Builder < Rack::Builder
6
+ class Rack
7
+ class Builder < ::Rack::Builder
7
8
  def initialize(&block)
8
- @usher = Usher::Interface::RackInterface.new
9
+ @usher = Usher::Interface::Rack.new
9
10
  super
10
11
  end
11
12
 
@@ -14,7 +15,10 @@ class Usher
14
15
  @ins << @usher unless @ins.last == @usher
15
16
  end
16
17
 
18
+ # it returns route, and because you may want to work with the route,
19
+ # for example give it a name, we returns the route with GET request
17
20
  def get(path, options = nil, &block)
21
+ self.map(path, options.merge!(:conditions => {:request_method => "HEAD"}), &block)
18
22
  self.map(path, options.merge!(:conditions => {:request_method => "GET"}), &block)
19
23
  end
20
24
 
@@ -35,7 +39,7 @@ class Usher
35
39
  attr_accessor :app
36
40
 
37
41
  def initialize(app = nil, &blk)
38
- @app = app || lambda { |env| Rack::Response.new("No route found", 404).finish }
42
+ @app = app || lambda { |env| ::Rack::Response.new("No route found", 404).finish }
39
43
  @router = Usher.new(:request_methods => [:request_method, :host, :port, :scheme], :generator => Usher::Util::Generators::URL.new)
40
44
  instance_eval(&blk) if blk
41
45
  end
@@ -63,11 +67,11 @@ class Usher
63
67
  # add("/url", :conditions => {:request_method => "POST"}})
64
68
  # is the same as:
65
69
  # post("/url")
66
- #
67
- # if you need more complex setup, use method add directly, for example:
68
- # add("/url", :conditions => {:request_method => ["POST", "PUT"]}})
70
+
71
+ # it returns route, and because you may want to work with the route,
72
+ # for example give it a name, we returns the route with GET request
69
73
  def get(path, options = {})
70
- self.add(path, options.merge!(:conditions => {:request_method => "GET"}))
74
+ self.add(path, options.merge!(:conditions => {:request_method => ["HEAD", "GET"]}))
71
75
  end
72
76
 
73
77
  def post(path, options = {})
@@ -95,7 +99,7 @@ class Usher
95
99
  end
96
100
 
97
101
  def call(env)
98
- request = Rack::Request.new(env)
102
+ request = ::Rack::Request.new(env)
99
103
  response = @router.recognize(request, request.path_info)
100
104
  after_match(request, response) if response
101
105
  determine_respondant(response).call(env)
@@ -0,0 +1,16 @@
1
+ class Usher
2
+ class Route
3
+ # add("/index.html").redirect("/")
4
+ def redirect(path, status = 302)
5
+ unless (300..399).include?(status)
6
+ raise ArgumentError, "Status has to be an integer between 300 and 399"
7
+ end
8
+ @destination = lambda do |env|
9
+ response = Rack::Response.new
10
+ response.redirect(path, status)
11
+ response.finish
12
+ end
13
+ return self
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ class Usher
2
+ module Interface
3
+ class Rails20 < Rails22
4
+ Mapper = Usher::Interface::Rails22::Mapper
5
+ end
6
+ end
7
+ end
@@ -1,8 +1,8 @@
1
- require File.join(File.dirname(__FILE__), 'rails2_2_interface', 'mapper')
1
+ require File.join('usher', 'interface', 'rails22', 'mapper')
2
2
 
3
3
  class Usher
4
4
  module Interface
5
- class Rails22Interface
5
+ class Rails22
6
6
 
7
7
  attr_reader :usher
8
8
  attr_accessor :configuration_file
@@ -10,9 +10,12 @@ class Usher
10
10
  def initialize
11
11
  reset!
12
12
  end
13
-
14
- def reset!
15
- @usher ||= Usher.new(:generator => Usher::Util::Generators::URL.new, :request_methods => [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains])
13
+
14
+ def reset!(options={})
15
+ options[:generator] = options[:generator] || Usher::Util::Generators::URL.new
16
+ options[:request_methods] = options[:request_methods] || [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains]
17
+
18
+ @usher = Usher.new(options)
16
19
  @module ||= Module.new
17
20
  @module.instance_methods.each do |selector|
18
21
  @module.class_eval { remove_method selector }
@@ -32,7 +35,7 @@ class Usher
32
35
  add_route('/:controller', options.merge({:action => 'index'}))
33
36
  @controller_route_added = true
34
37
  end
35
-
38
+
36
39
  options[:action] = 'index' unless options[:action]
37
40
 
38
41
  path[0, 0] = '/' unless path[0] == ?/
@@ -109,25 +112,22 @@ class Usher
109
112
  reload
110
113
  end
111
114
 
112
- def draw
113
- reset!
115
+ def draw(options={})
116
+ reset!(options)
114
117
  yield Mapper.new(self)
115
118
  install_helpers
116
119
  end
117
120
 
118
121
  def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
119
- #*_url and hash_for_*_url
120
- Array(destinations).each do |d| d.module_eval { include Helpers }
121
- @usher.named_routes.keys.each do |name|
122
- @module.module_eval <<-end_eval # We use module_eval to avoid leaks
123
- def #{name}_url(options = {})
124
- ActionController::Routing::UsherRoutes.generate(options, {}, :generate, :#{name})
125
- end
126
- end_eval
127
- end
128
- d.__send__(:include, @module)
122
+ Array(destinations).each do |destination|
123
+ destination.module_eval { include Helpers }
124
+ destination.__send__(:include, @usher.generator.generation_module)
129
125
  end
130
126
  end
127
+
128
+ def routes
129
+ @usher.routes
130
+ end
131
131
 
132
132
  end
133
133
  end
@@ -1,13 +1,13 @@
1
1
  class Usher
2
2
  module Interface
3
- class Rails22Interface
3
+ class Rails22
4
4
 
5
5
  class Mapper #:doc:
6
6
  def initialize(set) #:nodoc:
7
7
  @set = set
8
8
  end
9
9
 
10
- def connect(path, options = nil)
10
+ def connect(path, options = {})
11
11
  @set.add_route(path, options)
12
12
  end
13
13
 
@@ -1,6 +1,6 @@
1
1
  class Usher
2
2
  module Interface
3
- class Rails23Interface
3
+ class Rails23
4
4
 
5
5
  attr_reader :configuration_files
6
6
 
@@ -0,0 +1,44 @@
1
+ class Usher
2
+ module Interface
3
+ class Rails23
4
+
5
+ class Mapper #:doc:
6
+ def initialize(set) #:nodoc:
7
+ @set = set
8
+ end
9
+
10
+ def connect(path, options = {})
11
+ @set.add_route(path, options)
12
+ end
13
+
14
+ def root(options = {})
15
+ if options.is_a?(Symbol)
16
+ if source_route = @set.named_routes[options]
17
+ options = source_route.conditions.blank? ?
18
+ source_route.options.merge({ :conditions => source_route.conditions }) : source_route.options
19
+ end
20
+ end
21
+ named_route(:root, '/', options)
22
+ end
23
+
24
+ def named_route(name, path, options = nil)
25
+ @set.add_named_route(name, path, options)
26
+ end
27
+
28
+ def namespace(name, options = {}, &block)
29
+ if options[:namespace]
30
+ with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
31
+ else
32
+ with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block)
33
+ end
34
+ end
35
+
36
+ def method_missing(route_name, *args, &proc) #:nodoc:
37
+ super unless args.length >= 1 && proc.nil?
38
+ @set.add_named_route(route_name, *args)
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end