vigetlabs-routing-filter 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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