strikeroff-routing-filter 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,20 +1,20 @@
1
- Copyright (c) 2008 Sven Fuchs
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1
+ Copyright (c) 2008 Sven Fuchs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,20 @@
1
+ init.rb
2
+ lib/routing_filter/base.rb
3
+ lib/routing_filter/force_extension.rb
4
+ lib/routing_filter/locale.rb
5
+ lib/routing_filter/pagination.rb
6
+ lib/routing_filter.rb
7
+ Manifest
8
+ MIT-LICENSE
9
+ Rakefile
10
+ README.markdown
11
+ routing-filter.gemspec
12
+ spec/force_extension_spec.rb
13
+ spec/generation_spec.rb
14
+ spec/pagination_extension_spec.rb
15
+ spec/recognition_spec.rb
16
+ spec/routing_filter_spec.rb
17
+ spec/spec.opts
18
+ spec/spec_helper.rb
19
+ strikeroff-routing-filter.gemspec
20
+ VERSION
@@ -1,147 +1,151 @@
1
- ## Routing Filter
2
-
3
- This plugin is a wild hack that wraps around the complex beast that the Rails
4
- routing system is to allow for unseen flexibility and power in Rails URL
5
- recognition and generation.
6
-
7
- As powerful and awesome the Rails' routes are, when you need to design your
8
- URLs in a manner that only slightly leaves the paved cowpaths of Rails
9
- conventions, you're usually unable to use all the goodness of helpers and
10
- convenience that Rails ships with.
11
-
12
- ## Usage
13
-
14
- This plugin comes with a locale routing filter that demonstrates the
15
- implementation of a custom filter.
16
-
17
- The following would be a sceleton of an empty filter:
18
-
19
- module RoutingFilter
20
- class Awesomeness < Base
21
- def around_recognize(route, path, env)
22
- # Alter the path here before it gets recognized.
23
- # Make sure to yield (calls the next around filter if present and
24
- # eventually `recognize_path` on the routeset):
25
- returning yield do |params|
26
- # You can additionally modify the params here before they get passed
27
- # to the controller.
28
- end
29
- end
30
-
31
- def around_generate(controller, *args, &block)
32
- # Alter arguments here before they are passed to `url_for`.
33
- # Make sure to yield (calls the next around filter if present and
34
- # eventually `url_for` on the controller):
35
- returning yield do |result|
36
- # You can change the generated url_or_path here. Make sure to use
37
- # one of the "in-place" modifying String methods though (like sub!
38
- # and friends).
39
- end
40
- end
41
- end
42
- end
43
-
44
- You then have to specify the filter explicitely in your routes.rb:
45
-
46
- ActionController::Routing::Routes.draw do |map|
47
- map.filter 'awesomeness'
48
- end
49
-
50
- (I am not sure if it makes sense to provide more technical information than
51
- this because the usage of this plugin definitely requires some advanced
52
- knowledge about Rails internals and especially its routing system. So, I
53
- figure, anyone who could use this should also be able to read the code and
54
- figure out what it's doing much better then from any lengthy documentation.
55
-
56
- If I'm mistaken on this please drop me an email with your suggestions.)
57
-
58
-
59
- ## Rationale: Two example usecases
60
-
61
- An early usecase from which this originated was the need to define a locale
62
- at the beginning of an URL in a way so that
63
-
64
- * the locale can be omitted when it is the default locale
65
- * all the url\_helpers that are generated by named routes continue to work in
66
- a concise manner (i.e. without specifying all parameters again and again)
67
- * ideally also plays nicely with default route helpers in tests/specs
68
-
69
- You can read about this struggle and two possible, yet unsatisfying solutions
70
- [here](http://www.artweb-design.de/2007/5/13/concise-localized-rails-url-helpers-solved-twice).
71
- The conclusion so far is that Rails itself does not provide the tools to solve
72
- this problem in a clean and dry way.
73
-
74
- Another usecase that eventually spawned the manifestation of this plugin was
75
- the need to map an arbitrary count of path segments to a certain model
76
- instance. In an application that I've been working on recently I needed to
77
- map URL paths to a nested tree of models like so:
78
-
79
- root
80
- + docs
81
- + api
82
- + wiki
83
-
84
- E.g. the docs section should map to the path `/docs`, the api section to
85
- the path `/docs/api` and so on. Furthermore, after these paths there need to be
86
- more things to be specified. E.g. the wiki needs to define a whole Rails
87
- resource with URLs like `/docs/wiki/pages/1/edit`.
88
-
89
- The only way to solve this problem with Rails' routing toolkit is to map
90
- a big, bold `/*everything` catch-all ("globbing") route and process the whole
91
- path in a custom dispatcher.
92
-
93
- This, of course, is a really unsatisfying solution because one has to
94
- reimplement everything that Rails routes are here to help with: regarding both
95
- URL recognition (like parameter mappings, resources, ...) and generation
96
- (url\_helpers).
97
-
98
- ## Solution
99
-
100
- This plugin offers a solution that takes exactly the opposite route.
101
-
102
- Instead of trying to change things *between* the URL recognition and
103
- generation stages to achieve the desired result it *wraps around* the whole
104
- routing system and allows to pre- and post-filter both what goes into it
105
- (URL recognition) and what comes out of it (URL generation).
106
-
107
- This way we can leave *everything* else completely untouched.
108
-
109
- * We can tinker with the URLs that we receive from the server and feed URLs to
110
- Rails that perfectly match the best breed of Rails' conventions.
111
- * Inside of the application we can use all the nice helper goodness and
112
- conveniences that rely on these conventions being followed.
113
- * Finally we can accept URLs that have been generated by the url\_helpers and,
114
- again, mutate them in the way that matches our requirements.
115
-
116
- So, even though the plugin itself is a blatant monkey-patch to one of the
117
- most complex area of Rails internals, this solution seems to be effectively
118
- less intrusive and pricey than others are.
119
-
120
-
121
- # Difference from origin
122
-
123
- 1. Locale filter now has option :scip_locale_filter
124
- In you routes rb you can
125
- map.change_locale "main/change_locale/:new_locale", :controller=>"main", :action=>"change_locale", :scip_locale_filter=>true
126
- And locale filter wouldn't add locale to url while generate
127
-
128
- 2. extract_locale! and prepend_locale! methods become class methods aka static. So you can use them outside of gem
129
- Usecase:
130
- For example to create on page language changer without requests to served. You need only change locale in url
131
-
132
- Example code:
133
- Current locale - :ru,but you need to have a link with locale = :en.
134
-
135
- link_to("EN", url_with_locale(request.request_uri.dup, :en))
136
-
137
- def url_with_locale(url,locale)
138
- RoutingFilter::Locale.extract_locale! url
139
- RoutingFilter::Locale.prepend_locale! url, locale
140
- end
141
-
142
-
143
-
144
- ## Etc
145
-
146
- Authors: [Sven Fuchs](http://www.artweb-design.de) <svenfuchs at artweb-design dot de>
1
+ ## Routing Filter
2
+
3
+ This plugin is a wild hack that wraps around the complex beast that the Rails
4
+ routing system is to allow for unseen flexibility and power in Rails URL
5
+ recognition and generation.
6
+
7
+ As powerful and awesome the Rails' routes are, when you need to design your
8
+ URLs in a manner that only slightly leaves the paved cowpaths of Rails
9
+ conventions, you're usually unable to use all the goodness of helpers and
10
+ convenience that Rails ships with.
11
+
12
+ ## Usage
13
+
14
+ This plugin comes with a locale routing filter that demonstrates the
15
+ implementation of a custom filter.
16
+
17
+ The following would be a sceleton of an empty filter:
18
+
19
+ module RoutingFilter
20
+ class Awesomeness < Base
21
+ def around_recognize(route, path, env)
22
+ # Alter the path here before it gets recognized.
23
+ # Make sure to yield (calls the next around filter if present and
24
+ # eventually `recognize_path` on the routeset):
25
+ returning yield do |params|
26
+ # You can additionally modify the params here before they get passed
27
+ # to the controller.
28
+ end
29
+ end
30
+
31
+ def around_generate(controller, *args, &block)
32
+ # Alter arguments here before they are passed to `url_for`.
33
+ # Make sure to yield (calls the next around filter if present and
34
+ # eventually `url_for` on the controller):
35
+ returning yield do |result|
36
+ # You can change the generated url_or_path here. Make sure to use
37
+ # one of the "in-place" modifying String methods though (like sub!
38
+ # and friends).
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ You then have to specify the filter explicitely in your routes.rb:
45
+
46
+ ActionController::Routing::Routes.draw do |map|
47
+ map.filter 'awesomeness'
48
+ end
49
+
50
+ (I am not sure if it makes sense to provide more technical information than
51
+ this because the usage of this plugin definitely requires some advanced
52
+ knowledge about Rails internals and especially its routing system. So, I
53
+ figure, anyone who could use this should also be able to read the code and
54
+ figure out what it's doing much better then from any lengthy documentation.
55
+
56
+ If I'm mistaken on this please drop me an email with your suggestions.)
57
+
58
+
59
+ ## Rationale: Two example usecases
60
+
61
+ An early usecase from which this originated was the need to define a locale
62
+ at the beginning of an URL in a way so that
63
+
64
+ * the locale can be omitted when it is the default locale
65
+ * all the url\_helpers that are generated by named routes continue to work in
66
+ a concise manner (i.e. without specifying all parameters again and again)
67
+ * ideally also plays nicely with default route helpers in tests/specs
68
+
69
+ You can read about this struggle and two possible, yet unsatisfying solutions
70
+ [here](http://www.artweb-design.de/2007/5/13/concise-localized-rails-url-helpers-solved-twice).
71
+ The conclusion so far is that Rails itself does not provide the tools to solve
72
+ this problem in a clean and dry way.
73
+
74
+ Another usecase that eventually spawned the manifestation of this plugin was
75
+ the need to map an arbitrary count of path segments to a certain model
76
+ instance. In an application that I've been working on recently I needed to
77
+ map URL paths to a nested tree of models like so:
78
+
79
+ root
80
+ + docs
81
+ + api
82
+ + wiki
83
+
84
+ E.g. the docs section should map to the path `/docs`, the api section to
85
+ the path `/docs/api` and so on. Furthermore, after these paths there need to be
86
+ more things to be specified. E.g. the wiki needs to define a whole Rails
87
+ resource with URLs like `/docs/wiki/pages/1/edit`.
88
+
89
+ The only way to solve this problem with Rails' routing toolkit is to map
90
+ a big, bold `/*everything` catch-all ("globbing") route and process the whole
91
+ path in a custom dispatcher.
92
+
93
+ This, of course, is a really unsatisfying solution because one has to
94
+ reimplement everything that Rails routes are here to help with: regarding both
95
+ URL recognition (like parameter mappings, resources, ...) and generation
96
+ (url\_helpers).
97
+
98
+ ## Solution
99
+
100
+ This plugin offers a solution that takes exactly the opposite route.
101
+
102
+ Instead of trying to change things *between* the URL recognition and
103
+ generation stages to achieve the desired result it *wraps around* the whole
104
+ routing system and allows to pre- and post-filter both what goes into it
105
+ (URL recognition) and what comes out of it (URL generation).
106
+
107
+ This way we can leave *everything* else completely untouched.
108
+
109
+ * We can tinker with the URLs that we receive from the server and feed URLs to
110
+ Rails that perfectly match the best breed of Rails' conventions.
111
+ * Inside of the application we can use all the nice helper goodness and
112
+ conveniences that rely on these conventions being followed.
113
+ * Finally we can accept URLs that have been generated by the url\_helpers and,
114
+ again, mutate them in the way that matches our requirements.
115
+
116
+ So, even though the plugin itself is a blatant monkey-patch to one of the
117
+ most complex area of Rails internals, this solution seems to be effectively
118
+ less intrusive and pricey than others are.
119
+
120
+
121
+ # Difference from origin
122
+
123
+ 1. Locale filter now has option :scip_locale_filter.
124
+ In you routes.rb you can write this
125
+
126
+ map.change_locale "main/change_locale/:new_locale", :controller=>"main", :action=>"change_locale", :scip_locale_filter=>true
127
+
128
+ And locale filter wouldn't add locale to url while generate.
129
+
130
+ 2. extract_locale! and prepend_locale! methods become class methods aka static. So you can use them outside of gem.
131
+
132
+ Usecase:
133
+
134
+ For example, to create on page language changer without requests to server. You need only change locale in url.
135
+
136
+ Example code:
137
+ Current locale - :ru,but you need to have a link with locale = :en.
138
+
139
+ link_to("EN", url_with_locale(request.request_uri.dup, :en))
140
+
141
+ def url_with_locale(url,locale)
142
+ RoutingFilter::Locale.extract_locale! url
143
+ RoutingFilter::Locale.prepend_locale! url, locale
144
+ end
145
+
146
+
147
+
148
+ ## Etc
149
+
150
+ Authors: [Sven Fuchs](http://www.artweb-design.de) <svenfuchs at artweb-design dot de>
147
151
  License: MIT
@@ -0,0 +1,21 @@
1
+ # Rakefile
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'echoe'
5
+
6
+ Echoe.new('strikeroff-routing-filter', '0.1.0') do |p|
7
+ p.description = "strikeroff-routing-filter"
8
+ p.url = "http://github.com/strikeroff"
9
+ p.author = "Vesov Ilya"
10
+ p.email = "strikeroff@gmail.com"
11
+ p.ignore_pattern = ["tmp/*", "script/*",".svn",".git"]
12
+ p.need_tar_gz = false
13
+ p.retain_gemspec = true
14
+ p.gemspec_name = 'strikeroff-routing-filter.gemspec'
15
+ p.test_pattern = ["test/**/*_test.rb"]
16
+ p.rdoc_pattern = ["README", "CHANGELOG", "lib/**/*.rb"]
17
+ p.rdoc_options << "-c utf-8"
18
+ p.ignore_pattern = [".gitignore", "doc", ".idea", "*.bat","*.sh"]
19
+ end
20
+
21
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.2
@@ -1,94 +1,94 @@
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
18
-
19
- def predecessor(filter)
20
- ix = index(filter)
21
- ix > 0 ? self[ix - 1] : nil
22
- end
23
-
24
- def successor(filter)
25
- ix = index(filter)
26
- ix < length - 1 ? self[ix + 1] : nil
27
- 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
-
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]}"
51
- end
52
- end
53
- alias_method_chain :generate_optimisation_block, :filtering
54
- end
55
-
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
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
18
+
19
+ def predecessor(filter)
20
+ ix = index(filter)
21
+ ix > 0 ? self[ix - 1] : nil
22
+ end
23
+
24
+ def successor(filter)
25
+ ix = index(filter)
26
+ ix < length - 1 ? self[ix + 1] : nil
27
+ 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
+
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]}"
51
+ end
52
+ end
53
+ alias_method_chain :generate_optimisation_block, :filtering
54
+ end
55
+
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