routing-filter 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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}"