joshbuddy-usher 0.4.3 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
+ :patch: 5
2
3
  :major: 0
3
4
  :minor: 4
4
- :patch: 3
@@ -0,0 +1,82 @@
1
+ class Usher
2
+ class Generators
3
+
4
+ class URL
5
+
6
+ def initialize(usher)
7
+ @usher = usher
8
+ end
9
+
10
+ # Generates a completed URL based on a +route+ or set of optional +params+
11
+ #
12
+ # set = Usher.new
13
+ # route = set.add_named_route(:test_route, '/:controller/:action')
14
+ # set.generate_url(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true
15
+ # set.generate_url(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true
16
+ # set.generate_url(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true
17
+ def generate(routing_lookup, params = nil, options = nil)
18
+ delimiter = options && options.key?(:delimiter) ? options.delete(:delimiter) : @usher.delimiters.first
19
+
20
+ path = case routing_lookup
21
+ when Symbol
22
+ route = @usher.named_routes[routing_lookup]
23
+ params.is_a?(Hash) ? route.find_matching_path(params) : route.paths.first
24
+ when Route
25
+ params.is_a?(Hash) ? routing_lookup.find_matching_path(params) : routing_lookup.paths.first
26
+ when nil
27
+ params.is_a?(Hash) ? @usher.path_for_options(params) : raise
28
+ when Route::Path
29
+ routing_lookup
30
+ end
31
+
32
+ raise UnrecognizedException.new unless path
33
+
34
+ params = Array(params) if params.is_a?(String)
35
+ if params.is_a?(Array)
36
+ extra_params = params.last.is_a?(Hash) ? params.pop : nil
37
+ params = Hash[*path.dynamic_parts.inject([]){|a, dynamic_part| a.concat([dynamic_part.name, params.shift || raise(MissingParameterException.new)]); a}]
38
+ params.merge!(extra_params) if extra_params
39
+ end
40
+
41
+ result = ''
42
+ path.parts.each do |part|
43
+ case part
44
+ when Route::Variable
45
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
46
+ case part.type
47
+ when :*
48
+ value.each_with_index do |current_value, index|
49
+ current_value = current_value.to_s unless current_value.is_a?(String)
50
+ part.valid!(current_value)
51
+ result << current_value
52
+ result << delimiter if index != value.size - 1
53
+ end
54
+ when :':'
55
+ value = value.to_s unless value.is_a?(String)
56
+ part.valid!(value)
57
+ result << value
58
+ end
59
+ else
60
+ result << part
61
+ end
62
+ end
63
+ result = URI.escape(result)
64
+
65
+ if params && !params.empty?
66
+ has_query = result[??]
67
+ params.each do |k,v|
68
+ case v
69
+ when Array
70
+ v.each do |v_part|
71
+ result << (has_query ? '&' : has_query = true && '?') << CGI.escape("#{k.to_s}[]") << '=' << CGI.escape(v_part.to_s)
72
+ end
73
+ else
74
+ result << (has_query ? '&' : has_query = true && '?') << CGI.escape(k.to_s) << '=' << CGI.escape(v.to_s)
75
+ end
76
+ end
77
+ end
78
+ result
79
+ end
80
+ end
81
+ end
82
+ end
data/lib/usher/grapher.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'set'
2
-
3
1
  class Usher
4
2
  class Grapher
5
3
 
@@ -11,29 +9,53 @@ class Usher
11
9
  @significant_keys = nil
12
10
  @orders = Hash.new{|h,k| h[k] = Hash.new{|h2, k2| h2[k2] = []}}
13
11
  @key_count = Hash.new(0)
12
+ @cache = {}
14
13
  end
15
14
 
16
15
  def add_route(route)
17
16
  route.paths.each do |path|
18
- unless path.dynamic_set.size.zero?
19
- path.dynamic_set.each do |k|
20
- @orders[path.dynamic_set.size][k] << path
17
+ unless path.dynamic_keys.size.zero?
18
+ path.dynamic_keys.each do |k|
19
+ @orders[path.dynamic_keys.size][k] << path
21
20
  @key_count[k] += 1
22
21
  end
22
+
23
+ dynamic_parts_with_defaults = path.dynamic_parts.select{|part| part.default_value }.map{|dp| dp.name}
24
+ dynamic_parts_without_defaults = path.dynamic_parts.select{|part| !part.default_value }.map{|dp| dp.name}
25
+
26
+ (1...(2 ** (dynamic_parts_with_defaults.size))).each do |i|
27
+ current_set = dynamic_parts_without_defaults.dup
28
+ dynamic_parts_with_defaults.each_with_index do |dp, index|
29
+ current_set << dp unless (index & i) == 0
30
+ end
31
+
32
+ current_set.each do |k|
33
+ @orders[current_set.size][k] << path
34
+ @key_count[k] += 1
35
+ end
36
+ end
23
37
  end
24
38
  end
25
39
  end
26
40
 
27
41
  def significant_keys
28
- @significant_keys ||= Set.new(@key_count.keys)
42
+ @significant_keys ||= @key_count.keys.uniq
29
43
  end
30
44
 
31
45
  def find_matching_path(params)
32
46
  unless params.empty?
33
- set = Set.new(params.keys) & significant_keys
47
+ set = params.keys & significant_keys
48
+ if cached = @cache[set]
49
+ return cached
50
+ end
34
51
  set.size.downto(1) do |o|
35
52
  set.each do |k|
36
- @orders[o][k].each { |r| return r if r.dynamic_set.subset?(set) }
53
+ @orders[o][k].each { |r|
54
+ if r.can_generate_from?(set)
55
+ @cache[set] = r
56
+ return r
57
+ end
58
+ }
37
59
  end
38
60
  end
39
61
  nil
@@ -22,7 +22,7 @@ class Usher
22
22
  def act(email)
23
23
  response = @routes.recognize(email, email)
24
24
  if response.path
25
- response.path.route.params.first.call(response.params.inject({}){|h,(k,v)| h[k]=v.to_s; h })
25
+ response.path.route.destination.call(response.params.inject({}){|h,(k,v)| h[k]=v.to_s; h })
26
26
  end
27
27
  end
28
28
 
@@ -13,6 +13,7 @@ class Usher
13
13
 
14
14
  def initialize(&blk)
15
15
  @routes = Usher.new(:request_methods => RequestMethods)
16
+ @generator = Usher::Generators::URL.new(@routes)
16
17
  instance_eval(&blk) if blk
17
18
  end
18
19
 
@@ -26,8 +27,14 @@ class Usher
26
27
 
27
28
  def call(env)
28
29
  response = @routes.recognize(Request.new(env['REQUEST_URI'], env['REQUEST_METHOD'].downcase, env['HTTP_HOST'], env['SERVER_PORT'].to_i, env['rack.url_scheme']))
29
- env['usher.params'] = response.params.inject({}){|h,(k,v)| h[k]=v; h }
30
- response.path.route.params.first.call(env)
30
+ params = {}
31
+ response.params.each{ |hk| params[hk.first] = hk.last}
32
+ env['usher.params'] = params
33
+ response.path.route.destination.call(env)
34
+ end
35
+
36
+ def generate(route, params = nil, options = nil)
37
+ @generator.generate(route, params, options)
31
38
  end
32
39
 
33
40
  end
@@ -1,6 +1,6 @@
1
1
  class Usher
2
2
  module Interface
3
- class Rails2Interface
3
+ class Rails2_2Interface
4
4
 
5
5
  class Mapper #:doc:
6
6
  def initialize(set) #:nodoc:
@@ -1,10 +1,10 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
- require 'rails2_interface/mapper'
3
+ require 'rails2_2_interface/mapper'
4
4
 
5
5
  class Usher
6
6
  module Interface
7
- class Rails2Interface
7
+ class Rails2_2Interface
8
8
 
9
9
  attr_reader :usher
10
10
  attr_accessor :configuration_file
@@ -15,6 +15,7 @@ class Usher
15
15
 
16
16
  def reset!
17
17
  @usher ||= Usher.new
18
+ @url_generator ||= Usher::Generators::URL.new(@usher)
18
19
  @module ||= Module.new
19
20
  @module.instance_methods.each do |selector|
20
21
  @module.class_eval { remove_method selector }
@@ -39,14 +40,14 @@ class Usher
39
40
 
40
41
  path[0, 0] = '/' unless path[0] == ?/
41
42
  route = @usher.add_route(path, options)
42
- raise "your route must include a controller" unless route.primary_path.dynamic_set.include?(:controller) || route.params.first.include?(:controller)
43
+ raise "your route must include a controller" unless route.paths.first.dynamic_keys.include?(:controller) || route.destination.include?(:controller)
43
44
  route
44
45
  end
45
46
 
46
47
  def recognize(request)
47
48
  node = @usher.recognize(request)
48
49
  params = node.params.inject({}){|h,(k,v)| h[k]=v; h }
49
- request.path_parameters = (node.params.empty? ? node.path.route.params.first : node.path.route.params.first.merge(params)).with_indifferent_access
50
+ request.path_parameters = (node.params.empty? ? node.path.route.destination : node.path.route.destination.merge(params)).with_indifferent_access
50
51
  "#{request.path_parameters[:controller].camelize}Controller".constantize
51
52
  rescue
52
53
  raise ActionController::RoutingError, "No route matches #{request.path.inspect} with #{request.inspect}"
@@ -73,7 +74,7 @@ class Usher
73
74
  unless options.key?(:action)
74
75
  options[:action] = ''
75
76
  end
76
- route_for_options(merged_options)
77
+ path_for_options(merged_options)
77
78
  end
78
79
  case method
79
80
  when :generate
@@ -87,11 +88,11 @@ class Usher
87
88
  end
88
89
 
89
90
  def generate_url(route, params)
90
- @usher.generate_url(route, params)
91
+ @url_generator.generate(route, params)
91
92
  end
92
93
 
93
- def route_for_options(options)
94
- @usher.route_for_options(options)
94
+ def path_for_options(options)
95
+ @usher.path_for_options(options)
95
96
  end
96
97
 
97
98
  def named_routes
@@ -0,0 +1,137 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ class Usher
4
+ module Interface
5
+ class Rails2_3Interface
6
+
7
+ attr_reader :configuration_files
8
+
9
+ def named_routes
10
+ @router.named_routes
11
+ end
12
+
13
+ def add_named_route(name, route, options = {})
14
+ @router.add_route(route, options).name(name)
15
+ end
16
+
17
+ def add_route(path, options = {})
18
+ if !@controller_action_route_added && path =~ %r{^/?:controller/:action/:id$}
19
+ add_route('/:controller/:action', options.dup)
20
+ @controller_action_route_added = true
21
+ end
22
+
23
+ if !@controller_route_added && path =~ %r{^/?:controller/:action$}
24
+ add_route('/:controller', options.merge({:action => 'index'}))
25
+ @controller_route_added = true
26
+ end
27
+
28
+ options[:action] = 'index' unless options[:action]
29
+
30
+ path[0, 0] = '/' unless path[0] == ?/
31
+ route = @router.add_route(path, options).to(options)
32
+
33
+ raise "your route must include a controller" unless (route.paths.first.dynamic_keys.include?(:controller) || route.destination.include?(:controller))
34
+ route
35
+ end
36
+
37
+ def initialize
38
+ reset!
39
+ end
40
+
41
+ def add_configuration_file(file)
42
+ @configuration_files << file
43
+ end
44
+
45
+ def reload!
46
+ if configuration_files.any?
47
+ configuration_files.each { |config| load(config) }
48
+ else
49
+ add_route ":controller/:action/:id"
50
+ end
51
+
52
+ end
53
+ alias_method :reload, :reload!
54
+
55
+ def route_count
56
+ routes.size
57
+ end
58
+
59
+ def routes
60
+ @router.routes
61
+ end
62
+
63
+ def call(env)
64
+ request = ActionController::Request.new(env)
65
+ app = recognize(request)
66
+ app.call(env).to_a
67
+ end
68
+
69
+ def recognize(request)
70
+ response = @router.recognize(request)
71
+ request.path_parameters = (response.params.empty? ? response.path.route.destination : response.path.route.destination.merge(response.params.inject({}){|h,(k,v)| h[k]=v; h })).with_indifferent_access
72
+ response.params.each { |pair| request.path_parameters[pair.first] = pair.last }
73
+ "#{request.path_parameters[:controller].camelize}Controller".constantize
74
+ end
75
+
76
+ def reset!
77
+ @router = Usher.new
78
+ @url_generator = Usher::Generators::URL.new(@router)
79
+ @configuration_files = []
80
+ @module ||= Module.new
81
+ @controller_route_added = false
82
+ @controller_action_route_added = false
83
+ end
84
+
85
+ def draw
86
+ reset!
87
+ yield ActionController::Routing::RouteSet::Mapper.new(self)
88
+ install_helpers
89
+ end
90
+
91
+ def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
92
+ #*_url and hash_for_*_url
93
+ Array(destinations).each do |d| d.module_eval { include Helpers }
94
+ @router.named_routes.keys.each do |name|
95
+ @module.module_eval <<-end_eval # We use module_eval to avoid leaks
96
+ def #{name}_url(options = {})
97
+ ActionController::Routing::UsherRoutes.generate(options, {}, :generate, :#{name})
98
+ end
99
+ end_eval
100
+ end
101
+ d.__send__(:include, @module)
102
+ end
103
+ end
104
+
105
+ def generate(options, recall = {}, method = :generate, route_name = nil)
106
+ route = if(route_name)
107
+ @router.named_routes[route_name]
108
+ else
109
+ merged_options = options
110
+ merged_options[:controller] = recall[:controller] unless options.key?(:controller)
111
+ unless options.key?(:action)
112
+ options[:action] = ''
113
+ end
114
+ path_for_options(merged_options)
115
+ end
116
+ case method
117
+ when :generate
118
+ merged_options ||= recall.merge(options)
119
+ url = generate_url(route, merged_options)
120
+ url.slice!(-1) if url[-1] == ?/
121
+ url
122
+ else
123
+ raise "method #{method} not recognized"
124
+ end
125
+ end
126
+
127
+ def generate_url(route, params)
128
+ @url_generator.generate(route, params)
129
+ end
130
+
131
+ def path_for_options(options)
132
+ @router.path_for_options(options)
133
+ end
134
+
135
+ end
136
+ end
137
+ end
@@ -2,15 +2,18 @@ $:.unshift File.dirname(__FILE__)
2
2
 
3
3
  class Usher
4
4
  module Interface
5
- autoload :Rails2Interface, 'interface/rails2_interface'
5
+ autoload :Rails2_2Interface, 'interface/rails2_2_interface'
6
+ autoload :Rails2_3Interface, 'interface/rails2_3_interface'
6
7
  autoload :MerbInterface, 'interface/merb_interface'
7
8
  autoload :RackInterface, 'interface/rack_interface'
8
9
  autoload :EmailInterface, 'interface/email_interface'
9
10
 
10
11
  def self.for(type, &blk)
11
12
  case type
12
- when :rails2
13
- Rails2Interface.new(&blk)
13
+ when :rails2_2
14
+ Rails2_2Interface.new(&blk)
15
+ when :rails2_3
16
+ Rails2_3Interface.new(&blk)
14
17
  when :merb
15
18
  MerbInterface.new(&blk)
16
19
  when :rack
data/lib/usher/node.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
- require 'fuzzy_hash'
3
+ require '/Users/josh/Development/fuzzy_hash/lib/fuzzy_hash'
4
4
 
5
5
  class Usher
6
6
 
@@ -89,10 +89,10 @@ class Usher
89
89
  route
90
90
  end
91
91
 
92
- def find(request, path, params = [])
92
+ def find(usher, request, path, params = [])
93
93
  if exclusive_type
94
94
  [lookup[request.send(exclusive_type)], lookup[nil]].each do |n|
95
- if n && (ret = n.find(request, path.dup, params.dup))
95
+ if n && (ret = n.find(usher, request, path.dup, params.dup))
96
96
  return ret
97
97
  end
98
98
  end
@@ -105,15 +105,15 @@ class Usher
105
105
  when :*
106
106
  params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
107
107
  loop do
108
- if (next_part.value.look_ahead === part || (!part.is_a?(Symbol) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
108
+ if (next_part.value.look_ahead === part || (!usher.splitter.delimiter_chars.include?(part[0]) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
109
109
  path.unshift(part)
110
- path.unshift(next_part.parent.value) if next_part.parent.value.is_a?(Symbol)
110
+ path.unshift(next_part.parent.value) if usher.splitter.delimiter_chars.include?(next_part.parent.value[0])
111
111
  break
112
- else
113
- unless part.is_a?(Symbol) && !next_part.value.globs_capture_separators
114
- next_part.value.valid!(part)
115
- params.last.last << part
116
- end
112
+ elsif next_part.value.globs_capture_separators
113
+ params.last.last << part
114
+ elsif !usher.splitter.delimiter_chars.include?(part[0])
115
+ next_part.value.valid!(part)
116
+ params.last.last << part
117
117
  end
118
118
  if path.size.zero?
119
119
  break
@@ -122,15 +122,15 @@ class Usher
122
122
  end
123
123
  end
124
124
  when :':'
125
- next_part.value.valid!(part)
126
125
  var = next_part.value
127
- params << [next_part.value.name, part]
128
- until (path.first == var.look_ahead) || path.empty?
129
- params.last.last << path.shift.to_s
126
+ var.valid!(part)
127
+ params << [var.name, part]
128
+ until (var.look_ahead === path.first) || path.empty?
129
+ params.last.last << path.shift
130
130
  end
131
131
  end
132
132
  end
133
- next_part.find(request, path, params)
133
+ next_part.find(usher, request, path, params)
134
134
  else
135
135
  nil
136
136
  end