vigetlabs-routing-filter 0.2.4

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 @@
1
+ require 'routing_filter'
@@ -0,0 +1,27 @@
1
+ require 'action_pack'
2
+ require 'active_support/core_ext/string/inflections'
3
+
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)
15
+ end
16
+
17
+ def active=(active)
18
+ @@active = active
19
+ end
20
+
21
+ def active?
22
+ defined?(@@active) ? @@active : @@active = true
23
+ end
24
+ end
25
+ end
26
+
27
+ require "routing_filter/adapters/rails_#{ActionPack::VERSION::MAJOR}"
@@ -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
+ (ActionPack::VERSION::MINOR >= 3 && ActionPack::VERSION::TINY >= 9 ? "#{match[1]}.tap { |result|" : "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.unshift(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
+ extract_request_environment_without_host(request).tap 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,77 @@
1
+ require 'action_dispatch'
2
+ require 'active_support/core_ext/module/aliasing'
3
+ require 'active_support/core_ext/hash/reverse_merge'
4
+
5
+ mappers = [ActionDispatch::Routing::Mapper]
6
+ mappers << ActionDispatch::Routing::DeprecatedMapper if defined?(ActionDispatch::Routing::DeprecatedMapper)
7
+ mappers.each do |mapper|
8
+ mapper.class_eval do
9
+ def filter(*args)
10
+ @set.add_filters(*args)
11
+ end
12
+ end
13
+ end
14
+
15
+ ActionDispatch::Routing::RouteSet.class_eval do
16
+ def filters
17
+ @set.filters if @set
18
+ end
19
+
20
+ def add_filters(*names)
21
+ options = names.extract_options!
22
+ names.each { |name| filters.unshift(RoutingFilter.build(name, options)) }
23
+ end
24
+
25
+ # def recognize_path_with_filtering(path, env = {})
26
+ # @set.filters.run(:around_recognize, path.dup, env, &lambda{ recognize_path_without_filtering(path.dup, env) })
27
+ # end
28
+ # alias_method_chain :recognize_path, :filtering
29
+
30
+ def generate_with_filtering(options, recall = {}, extras = false)
31
+ filters.run(:around_generate, options, &lambda{ generate_without_filtering(options, recall, extras) })
32
+ end
33
+ alias_method_chain :generate, :filtering
34
+
35
+ def clear_with_filtering!
36
+ filters.clear if filters
37
+ clear_without_filtering!
38
+ end
39
+ alias_method_chain :clear!, :filtering
40
+ end
41
+
42
+ require 'rack/mount/route_set'
43
+ require 'rack/mount/code_generation'
44
+
45
+ Rack::Mount::RouteSet.class_eval do
46
+ def filters
47
+ @filters || RoutingFilter::Chain.new.tap { |f| @filters = f unless frozen? }
48
+ end
49
+ end
50
+
51
+ # gah. so who's hoped monkeypatching optimized code wouldn't be necessary with rails 3 anymore?
52
+ Rack::Mount::CodeGeneration.class_eval do
53
+ def optimize_recognize_with_filtering!
54
+ optimize_recognize_without_filtering!
55
+ (class << self; self; end).class_eval do
56
+ alias_method_chain :recognize, :filtering
57
+ end
58
+ end
59
+ alias :optimize_recognize_without_filtering! :optimize_recognize!
60
+ alias :optimize_recognize! :optimize_recognize_with_filtering!
61
+
62
+ # note: if you overly and unnecessarily use blocks in your lowlevel libraries you make it fricking
63
+ # hard for your users to hook in anywhere
64
+ def recognize_with_filtering(request, &block)
65
+ path, route, matches, params = request.env['PATH_INFO'], nil, nil, nil
66
+ original_path = path.dup
67
+
68
+ filters.run(:around_recognize, path, request.env) do
69
+ route, matches, params = recognize_without_filtering(request)
70
+ params || {}
71
+ end
72
+
73
+ request.env['PATH_INFO'] = original_path # hmm ...
74
+ block.call(route, matches, params) if route
75
+ end
76
+ end
77
+
@@ -0,0 +1,22 @@
1
+ module RoutingFilter
2
+ class Chain < Array
3
+ def <<(filter)
4
+ filter.previous, last.next = last, filter if last
5
+ super
6
+ end
7
+ alias push <<
8
+
9
+ def unshift(filter)
10
+ filter.next, first.previous = first, filter if first
11
+ super
12
+ end
13
+
14
+ def run(method, *args, &final)
15
+ active? ? first.run(method, *args, &final) : final.call
16
+ end
17
+
18
+ def active?
19
+ RoutingFilter.active? && !empty?
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
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 ? proc {|path, env| 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
+
19
+ protected
20
+
21
+ def extract_segment!(pattern, path)
22
+ path.sub!(pattern) { $2 || '' }
23
+ path.replace('/') if path.empty?
24
+ $1
25
+ end
26
+
27
+ def prepend_segment!(result, segment)
28
+ url = result.is_a?(Array) ? result.first : result
29
+ url.sub!(%r(^(http.?://[^/]*)?(.*))) { "#{$1}/#{segment}#{$2 == '/' ? '' : $2}" }
30
+ end
31
+
32
+ def append_segment!(result, segment)
33
+ url = result.is_a?(Array) ? result.first : result
34
+ url.sub!(%r(/?($|\?))) { "/#{segment}#{$1}" }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,76 @@
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'))
21
+
22
+ module RoutingFilter
23
+ class Extension < Filter
24
+ attr_reader :extension, :exclude
25
+
26
+ def initialize(*args)
27
+ super
28
+ @exclude = options[:exclude]
29
+ @extension = options[:extension] || 'html'
30
+ end
31
+
32
+ def around_recognize(path, env, &block)
33
+ extract_extension!(path) unless excluded?(path)
34
+ yield(path, env)
35
+ end
36
+
37
+ def around_generate(params, &block)
38
+ yield.tap do |result|
39
+ url = result.is_a?(Array) ? result.first : result
40
+ append_extension!(url) if append_extension?(url)
41
+ end
42
+ end
43
+
44
+ protected
45
+
46
+ def extract_extension!(path)
47
+ path.sub!(/\.#{extension}$/, '')
48
+ $1
49
+ end
50
+
51
+ def append_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(^/(\?|$)))
61
+ end
62
+
63
+ def excluded?(url)
64
+ case exclude
65
+ when Regexp
66
+ url =~ exclude
67
+ when Proc
68
+ exclude.call(url)
69
+ end
70
+ end
71
+
72
+ def mime_extension?(url)
73
+ url =~ /\.#{Mime::EXTENSION_LOOKUP.keys.join('|')}(\?|$)/
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,83 @@
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
+
23
+ require 'i18n'
24
+
25
+ module RoutingFilter
26
+ class Locale < Filter
27
+ @@include_default_locale = true
28
+ cattr_writer :include_default_locale
29
+
30
+ class << self
31
+ def include_default_locale?
32
+ @@include_default_locale
33
+ end
34
+
35
+ def locales
36
+ @@locales ||= I18n.available_locales.map(&:to_sym)
37
+ end
38
+
39
+ def locales=(locales)
40
+ @@locales = locales.map(&:to_sym)
41
+ end
42
+
43
+ def locales_pattern
44
+ @@locales_pattern ||= %r(^/(#{self.locales.map { |l| Regexp.escape(l.to_s) }.join('|')})(?=/|$))
45
+ end
46
+ end
47
+
48
+ def around_recognize(path, env, &block)
49
+ locale = extract_segment!(self.class.locales_pattern, path) # remove the locale from the beginning of the path
50
+ yield.tap do |params| # invoke the given block (calls more filters and finally routing)
51
+ params[:locale] = locale if locale # set recognized locale to the resulting params hash
52
+ end
53
+ end
54
+
55
+ def around_generate(*args, &block)
56
+ params = args.extract_options! # this is because we might get a call like forum_topics_path(forum, topic, :locale => :en)
57
+
58
+ locale = params.delete(:locale) # extract the passed :locale option
59
+ locale = I18n.locale if locale.nil? # default to I18n.locale when locale is nil (could also be false)
60
+ locale = nil unless valid_locale?(locale) # reset to no locale when locale is not valid
61
+
62
+ args << params
63
+
64
+ yield.tap do |result|
65
+ prepend_segment!(result, locale) if prepend_locale?(locale)
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ def valid_locale?(locale)
72
+ locale && self.class.locales.include?(locale.to_sym)
73
+ end
74
+
75
+ def default_locale?(locale)
76
+ locale && locale.to_sym == I18n.default_locale.to_sym
77
+ end
78
+
79
+ def prepend_locale?(locale)
80
+ locale && (self.class.include_default_locale? || !default_locale?(locale))
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,47 @@
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
+ PAGINATION_SEGMENT = %r(/page/([\d]+)/?$)
26
+
27
+ def around_recognize(path, env, &block)
28
+ page = extract_segment!(PAGINATION_SEGMENT, path)
29
+ yield(path, env).tap do |params|
30
+ params[:page] = page.to_i if page
31
+ end
32
+ end
33
+
34
+ def around_generate(params, &block)
35
+ page = params.delete(:page)
36
+ yield.tap do |result|
37
+ append_segment!(result, "page/#{page}") if append_page?(page)
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def append_page?(page)
44
+ page && page.to_i != 1
45
+ end
46
+ end
47
+ end