routing-filter 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ require 'action_controller'
2
+
3
+ # allows to install a filter to the route set by calling: map.filter 'locale'
4
+ ActionController::Routing::RouteSet::Mapper.class_eval do
5
+ def filter(*args)
6
+ @set.add_filters(*args)
7
+ end
8
+ end
9
+
10
+ # same here for the optimized url generation in named routes
11
+ ActionController::Routing::RouteSet::NamedRouteCollection.class_eval do
12
+ # gosh. monkey engineering optimization code
13
+ def generate_optimisation_block_with_filtering(*args)
14
+ code = generate_optimisation_block_without_filtering(*args)
15
+ if match = code.match(%r(^return (.*) if (.*)))
16
+ # returned string must not contain newlines, or we'll spill out of inline code comments in
17
+ # ActionController::Routing::RouteSet::NamedRouteCollection#define_url_helper
18
+ "returning(#{match[1]}) { |result|" +
19
+ " ActionController::Routing::Routes.filters.run(:around_generate, *args, &lambda{ result }) " +
20
+ "} if #{match[2]}"
21
+ end
22
+ end
23
+ alias_method_chain :generate_optimisation_block, :filtering
24
+ end
25
+
26
+ ActionController::Routing::RouteSet.class_eval do
27
+ attr_writer :filters
28
+
29
+ def filters
30
+ @filters ||= RoutingFilter::Chain.new
31
+ end
32
+
33
+ def add_filters(*names)
34
+ options = names.extract_options!
35
+ names.each { |name| filters << RoutingFilter.build(name, options) }
36
+ end
37
+
38
+ def recognize_path_with_filtering(path, env = {})
39
+ path = ::URI.unescape(path.dup) # string is frozen due to memoize
40
+ filters.run(:around_recognize, path, env, &lambda{ recognize_path_without_filtering(path, env) })
41
+ end
42
+ alias_method_chain :recognize_path, :filtering
43
+
44
+ def generate_with_filtering(*args)
45
+ filters.run(:around_generate, args.first, &lambda{ generate_without_filtering(*args) })
46
+ end
47
+ alias_method_chain :generate, :filtering
48
+
49
+ def clear_with_filtering!
50
+ @filters.clear if @filters
51
+ clear_without_filtering!
52
+ end
53
+ alias_method_chain :clear!, :filtering
54
+
55
+ # add some useful information to the request environment
56
+ # right, this is from jamis buck's excellent article about routes internals
57
+ # http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2
58
+ # TODO move this ... where?
59
+ alias_method :extract_request_environment_without_host, :extract_request_environment unless method_defined? :extract_request_environment_without_host
60
+ def extract_request_environment(request)
61
+ returning extract_request_environment_without_host(request) do |env|
62
+ env.merge! :host => request.host,
63
+ :port => request.port,
64
+ :host_with_port => request.host_with_port,
65
+ :domain => request.domain,
66
+ :subdomain => request.subdomains.first
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,64 @@
1
+ require 'action_dispatch'
2
+ require 'active_support/core_ext/module/aliasing'
3
+ require 'active_support/core_ext/hash/reverse_merge'
4
+
5
+ [ActionDispatch::Routing::Mapper, ActionDispatch::Routing::DeprecatedMapper].each do |mapper|
6
+ mapper.class_eval do
7
+ def filter(*args)
8
+ @set.add_filters(*args)
9
+ end
10
+ end
11
+ end
12
+
13
+ ActionDispatch::Routing::RouteSet.class_eval do
14
+ def add_filters(*names)
15
+ options = names.extract_options!
16
+ names.each { |name| @set.filters << RoutingFilter.build(name, options) }
17
+ end
18
+
19
+ def recognize_path_with_filtering(path, env = {})
20
+ @set.filters.run(:around_recognize, path, env, &lambda{ recognize_path_without_filtering(path, env) })
21
+ end
22
+ alias_method_chain :recognize_path, :filtering
23
+
24
+ def generate_with_filtering(*args)
25
+ @set.filters.run(:around_generate, args.first, &lambda{ generate_without_filtering(*args) })
26
+ end
27
+ alias_method_chain :generate, :filtering
28
+
29
+ def clear_with_filtering!
30
+ @set.filters.clear if @set
31
+ clear_without_filtering!
32
+ end
33
+ alias_method_chain :clear!, :filtering
34
+ end
35
+
36
+ require 'rack/mount/route_set'
37
+ require 'rack/mount/code_generation'
38
+
39
+ Rack::Mount::RouteSet.class_eval do
40
+ def filters
41
+ @filters ||= RoutingFilter::Chain.new
42
+ end
43
+ end
44
+
45
+ # gah. so who's hoped monkeypatching optimized code wouldn't be necessary with rails 3 anymore?
46
+ Rack::Mount::CodeGeneration.class_eval do
47
+ def optimize_recognize_with_filtering!
48
+ optimize_recognize_without_filtering!
49
+ (class << self; self; end).class_eval do
50
+ alias_method_chain :recognize, :filtering
51
+ end
52
+ end
53
+ alias :optimize_recognize_without_filtering! :optimize_recognize!
54
+ alias :optimize_recognize! :optimize_recognize_with_filtering!
55
+
56
+ def recognize_with_filtering(request, &block)
57
+ filters.run(:around_recognize, request.env['PATH_INFO'], {}, &lambda{ recognize_without_filtering(request, &block) })
58
+ end
59
+ end
60
+
61
+
62
+
63
+
64
+
@@ -0,0 +1,16 @@
1
+ module RoutingFilter
2
+ class Chain < Array
3
+ def <<(filter)
4
+ filter.previous, last.next = last, filter if last
5
+ super
6
+ end
7
+
8
+ def run(method, *args, &final)
9
+ RoutingFilter.active? ? first.run(method, *args, &final) : final.call
10
+ end
11
+
12
+ def active?
13
+ RoutingFilter.active && !empty?
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ module RoutingFilter
2
+ class Filter
3
+ attr_accessor :next, :previous, :options
4
+
5
+ def initialize(options = {})
6
+ @options = options
7
+ end
8
+
9
+ def run(method, *args, &block)
10
+ _next = self.next ? lambda { self.next.run(method, *args, &block) } : block
11
+ RoutingFilter.active? ? send(method, *args, &_next) : _next.call(*args)
12
+ end
13
+
14
+ def run_reverse(method, *args, &block)
15
+ _prev = previous ? lambda { previous.run_reverse(method, *args, &block) } : block
16
+ RoutingFilter.active? ? send(method, *args, &_prev) : _prev.call(*args)
17
+ end
18
+ end
19
+ end
@@ -1,13 +1,32 @@
1
- require 'routing_filter/base'
1
+ # The Extension filter chops a file extension off from the end of the
2
+ # recognized path. When a path is generated the filter re-adds the extension
3
+ # to the path accordingly.
4
+ #
5
+ # incoming url: /de/products/page/1
6
+ # filtered url: /de/products
7
+ # params: params[:locale] = 'de'
8
+ #
9
+ # You can install the filter like this:
10
+ #
11
+ # # in config/routes.rb
12
+ # Rails.application.routes.draw do
13
+ # filter :locale
14
+ # end
15
+ #
16
+ # To make your named_route helpers or url_for add the pagination segments you
17
+ # can use:
18
+ #
19
+ # products_path(:locale => 'de')
20
+ # url_for(:products, :locale => 'de'))
2
21
 
3
22
  module RoutingFilter
4
- class ForceExtension < Base
23
+ class Extension < Filter
5
24
  attr_reader :extension, :exclude
6
25
 
7
26
  def initialize(*args)
8
27
  super
9
- @extension ||= 'html'
10
- @exclude = %r(^(http.?://[^/]+)?\/?$) if @exclude.nil?
28
+ @exclude = options[:exclude]
29
+ @extension = options[:extension] || 'html'
11
30
  end
12
31
 
13
32
  def around_recognize(path, env, &block)
@@ -16,21 +35,29 @@ module RoutingFilter
16
35
  end
17
36
 
18
37
  def around_generate(*args, &block)
19
- returning yield do |result|
38
+ yield.tap do |result|
20
39
  url = result.is_a?(Array) ? result.first : result
21
40
  append_extension!(url) if append_extension?(url)
22
41
  end
23
42
  end
24
43
 
25
44
  protected
26
-
45
+
27
46
  def extract_extension!(path)
28
- path.sub! /\.#{extension}$/, ''
47
+ path.sub!(/\.#{extension}$/, '')
29
48
  $1
30
49
  end
31
50
 
32
51
  def append_extension?(url)
33
- !(url.blank? || excluded?(url) || mime_extension?(url))
52
+ !(blank?(url) || excluded?(url) || mime_extension?(url))
53
+ end
54
+
55
+ def append_extension!(url)
56
+ url.replace url.sub(/(\?|$)/, ".#{extension}\\1")
57
+ end
58
+
59
+ def blank?(url)
60
+ url.blank? || !!url.match(%r(^/(\?|$)))
34
61
  end
35
62
 
36
63
  def excluded?(url)
@@ -45,13 +72,5 @@ module RoutingFilter
45
72
  def mime_extension?(url)
46
73
  url =~ /\.#{Mime::EXTENSION_LOOKUP.keys.join('|')}(\?|$)/
47
74
  end
48
-
49
- def append_extension!(url)
50
- url.replace url.sub(/(\?|$)/, ".#{extension}\\1")
51
- end
52
-
53
- def append_page!(url, page)
54
- url.replace "#{url}/pages/#{page}"
55
- end
56
75
  end
57
76
  end
@@ -1,8 +1,29 @@
1
+ # The Locale filter extracts segments matching /:locale from the beginning of
2
+ # the recognized path and exposes the page parameter as params[:page]. When a
3
+ # path is generated the filter adds the segments to the path accordingly if
4
+ # the page parameter is passed to the url helper.
5
+ #
6
+ # incoming url: /de/products/page/1
7
+ # filtered url: /de/products
8
+ # params: params[:locale] = 'de'
9
+ #
10
+ # You can install the filter like this:
11
+ #
12
+ # # in config/routes.rb
13
+ # Rails.application.routes.draw do
14
+ # filter :locale
15
+ # end
16
+ #
17
+ # To make your named_route helpers or url_for add the pagination segments you
18
+ # can use:
19
+ #
20
+ # products_path(:locale => 'de')
21
+ # url_for(:products, :locale => 'de'))
22
+
1
23
  require 'i18n'
2
- require 'routing_filter/base'
3
24
 
4
25
  module RoutingFilter
5
- class Locale < Base
26
+ class Locale < Filter
6
27
  @@include_default_locale = true
7
28
  cattr_writer :include_default_locale
8
29
 
@@ -26,7 +47,7 @@ module RoutingFilter
26
47
 
27
48
  def around_recognize(path, env, &block)
28
49
  locale = extract_locale!(path) # remove the locale from the beginning of the path
29
- returning yield do |params| # invoke the given block (calls more filters and finally routing)
50
+ yield.tap do |params| # invoke the given block (calls more filters and finally routing)
30
51
  params[:locale] = locale if locale # set recognized locale to the resulting params hash
31
52
  end
32
53
  end
@@ -36,25 +57,18 @@ module RoutingFilter
36
57
  locale = I18n.locale if locale.nil? # default to I18n.locale when locale is nil (could also be false)
37
58
  locale = nil unless valid_locale?(locale) # reset to no locale when locale is not valid
38
59
 
39
- returning yield do |result|
40
- if locale && prepend_locale?(locale)
41
- url = result.is_a?(Array) ? result.first : result
42
- prepend_locale!(url, locale)
43
- end
60
+ yield.tap do |result|
61
+ prepend_locale!(result, locale) if prepend_locale?(locale)
44
62
  end
45
63
  end
46
64
 
47
65
  protected
48
66
 
49
67
  def extract_locale!(path)
50
- path.sub! self.class.locales_pattern, ''
68
+ path.sub!(self.class.locales_pattern, '')
51
69
  $1
52
70
  end
53
71
 
54
- def prepend_locale?(locale)
55
- self.class.include_default_locale? || !default_locale?(locale)
56
- end
57
-
58
72
  def valid_locale?(locale)
59
73
  locale && self.class.locales.include?(locale.to_sym)
60
74
  end
@@ -63,7 +77,12 @@ module RoutingFilter
63
77
  locale && locale.to_sym == I18n.default_locale.to_sym
64
78
  end
65
79
 
66
- def prepend_locale!(url, locale)
80
+ def prepend_locale?(locale)
81
+ locale && (self.class.include_default_locale? || !default_locale?(locale))
82
+ end
83
+
84
+ def prepend_locale!(result, locale)
85
+ url = result.is_a?(Array) ? result.first : result
67
86
  url.sub!(%r(^(http.?://[^/]*)?(.*))) { "#{$1}/#{locale}#{$2}" }
68
87
  end
69
88
  end
@@ -0,0 +1,51 @@
1
+ # The Pagination filter extracts segments matching /page/:page from the end of
2
+ # the recognized url and exposes the page parameter as params[:page]. When a
3
+ # url is generated the filter adds the segments to the url accordingly if the
4
+ # page parameter is passed to the url helper.
5
+ #
6
+ # incoming url: /products/page/1
7
+ # filtered url: /products
8
+ # params: params[:page] = 1
9
+ #
10
+ # You can install the filter like this:
11
+ #
12
+ # # in config/routes.rb
13
+ # Rails.application.routes.draw do
14
+ # filter :pagination
15
+ # end
16
+ #
17
+ # To make your named_route helpers or url_for add the pagination segments you
18
+ # can use:
19
+ #
20
+ # products_path(:page => 1)
21
+ # url_for(:products, :page => 1)
22
+
23
+ module RoutingFilter
24
+ class Pagination < Filter
25
+ def around_recognize(path, env, &block)
26
+ page = extract_page!(path)
27
+ yield(path, env).tap do |params|
28
+ params[:page] = page.to_i if page
29
+ end
30
+ end
31
+
32
+ def around_generate(*args, &block)
33
+ page = args.extract_options!.delete(:page)
34
+ yield.tap do |result|
35
+ append_page!(result, page) if page && page.to_i != 1
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def extract_page!(path)
42
+ path.sub! %r(/page/([\d]+)/?$), ''
43
+ $1
44
+ end
45
+
46
+ def append_page!(result, page)
47
+ url = result.is_a?(Array) ? result.first : result
48
+ url.sub!(/($|\?)/) { "/page/#{page}#{$1}" }
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,52 @@
1
+ # The Uuid filter extracts an UUID segment from the beginning of the recognized
2
+ # path and exposes the page parameter as params[:page]. When a path is generated
3
+ # the filter adds the segments to the path accordingly if the page parameter is
4
+ # passed to the url helper.
5
+ #
6
+ # incoming url: /d00fbbd1-82b6-4c1a-a57d-098d529d6854/product/1
7
+ # filtered url: /product/1
8
+ # params: params[:uuid] = 'd00fbbd1-82b6-4c1a-a57d-098d529d6854'
9
+ #
10
+ # You can install the filter like this:
11
+ #
12
+ # # in config/routes.rb
13
+ # Rails.application.routes.draw do
14
+ # filter :uuid
15
+ # end
16
+ #
17
+ # To make your named_route helpers or url_for add the uuid segment you can use:
18
+ #
19
+ # product_path(:uuid => uuid)
20
+ # url_for(product, :uuid => uuid)
21
+
22
+ module RoutingFilter
23
+ class Uuid < Filter
24
+ UUID_SEGMENT = %r(^/?([a-z\d]{8}\-[a-z\d]{4}\-[a-z\d]{4}\-[a-z\d]{4}\-[a-z\d]{12})/)
25
+
26
+ def around_recognize(path, env, &block)
27
+ uuid = extract_uuid!(path)
28
+ yield.tap do |params|
29
+ params[:uuid] = uuid if uuid
30
+ end
31
+ end
32
+
33
+ def around_generate(*args, &block)
34
+ uuid = args.extract_options!.delete(:uuid)
35
+ yield.tap do |result|
36
+ prepend_uuid!(result, uuid) if uuid
37
+ end
38
+ end
39
+
40
+ protected
41
+
42
+ def extract_uuid!(path)
43
+ path.sub!(UUID_SEGMENT, '/')
44
+ $1
45
+ end
46
+
47
+ def prepend_uuid!(result, uuid)
48
+ url = result.is_a?(Array) ? result.first : result
49
+ url.sub!(%r(^(http.?://[^/]*)?(.*))) { "#{$1}/#{uuid}#{$2}" }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module RoutingFilter
2
+ VERSION = '0.1.0'
3
+ end
@@ -1,94 +1,27 @@
1
- module RoutingFilter
2
- mattr_accessor :active
3
- @@active = true
4
-
5
- class Chain < Array
6
- def << (filter)
7
- filter.chain = self
8
- super
9
- end
10
-
11
- def run(method, *args, &final)
12
- RoutingFilter.active && !first.nil? ? first.run(method, *args, &final) : final.call
13
- end
14
-
15
- def run_reverse(method, *args, &final)
16
- RoutingFilter.active && !last.nil? ? last.run_reverse(method, *args, &final) : final.call
17
- end
1
+ require 'action_pack'
2
+ require 'active_support/core_ext/string/inflections'
18
3
 
19
- def predecessor(filter)
20
- ix = index(filter)
21
- ix > 0 ? self[ix - 1] : nil
4
+ module RoutingFilter
5
+ autoload :Filter, 'routing_filter/filter'
6
+ autoload :Chain, 'routing_filter/chain'
7
+ autoload :Extension, 'routing_filter/filters/extension'
8
+ autoload :Locale, 'routing_filter/filters/locale'
9
+ autoload :Pagination, 'routing_filter/filters/pagination'
10
+ autoload :Uuid, 'routing_filter/filters/uuid'
11
+
12
+ class << self
13
+ def build(name, options)
14
+ const_get(name.to_s.camelize).new(options)
22
15
  end
23
16
 
24
- def successor(filter)
25
- ix = index(filter)
26
- ix < length - 1 ? self[ix + 1] : nil
17
+ def active=(active)
18
+ @@active = active
27
19
  end
28
- end
29
- end
30
-
31
- # allows to install a filter to the route set by calling: map.filter 'locale'
32
- ActionController::Routing::RouteSet::Mapper.class_eval do
33
- def filter(name, options = {})
34
- require options.delete(:file) || "routing_filter/#{name}"
35
- klass = RoutingFilter.const_get(name.to_s.camelize)
36
- @set.filters << klass.new(options)
37
- end
38
- end
39
20
 
40
- # same here for the optimized url generation in named routes
41
- ActionController::Routing::RouteSet::NamedRouteCollection.class_eval do
42
- # gosh. monkey engineering optimization code
43
- def generate_optimisation_block_with_filtering(*args)
44
- code = generate_optimisation_block_without_filtering(*args)
45
- if match = code.match(%r(^return (.*) if (.*)))
46
- # returned string must not contain newlines, or we'll spill out of inline code comments in
47
- # ActionController::Routing::RouteSet::NamedRouteCollection#define_url_helper
48
- "returning(#{match[1]}) { |result|" +
49
- " ActionController::Routing::Routes.filters.run_reverse(:around_generate, *args, &lambda{ result }) " +
50
- "} if #{match[2]}"
21
+ def active?
22
+ defined?(@@active) ? @@active : @@active = true
51
23
  end
52
24
  end
53
- alias_method_chain :generate_optimisation_block, :filtering
54
25
  end
55
26
 
56
- ActionController::Routing::RouteSet.class_eval do
57
- def clear_with_filtering!
58
- @filters.clear if @filters
59
- clear_without_filtering!
60
- end
61
- alias_method_chain :clear!, :filtering
62
-
63
- attr_writer :filters
64
-
65
- def filters
66
- @filters ||= RoutingFilter::Chain.new
67
- end
68
-
69
- def recognize_path_with_filtering(path, env = {})
70
- path = ::URI.unescape(path.dup) # string is frozen due to memoize
71
- filters.run(:around_recognize, path, env, &lambda{ recognize_path_without_filtering(path, env) })
72
- end
73
- alias_method_chain :recognize_path, :filtering
74
-
75
- def generate_with_filtering(*args)
76
- filters.run_reverse(:around_generate, args.first, &lambda{ generate_without_filtering(*args) })
77
- end
78
- alias_method_chain :generate, :filtering
79
-
80
- # add some useful information to the request environment
81
- # right, this is from jamis buck's excellent article about routes internals
82
- # http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2
83
- # TODO move this ... where?
84
- alias_method :extract_request_environment_without_host, :extract_request_environment unless method_defined? :extract_request_environment_without_host
85
- def extract_request_environment(request)
86
- returning extract_request_environment_without_host(request) do |env|
87
- env.merge! :host => request.host,
88
- :port => request.port,
89
- :host_with_port => request.host_with_port,
90
- :domain => request.domain,
91
- :subdomain => request.subdomains.first
92
- end
93
- end
94
- end
27
+ require "routing_filter/adapters/rails_#{ActionPack::VERSION::MAJOR}"