actionpack-direct_routes 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +39 -0
  4. data/Rakefile +34 -0
  5. data/lib/action_dispatch/routing/direct_routes/mapper.rb +17 -0
  6. data/lib/action_dispatch/routing/direct_routes/mapper/scope.rb +15 -0
  7. data/lib/action_dispatch/routing/direct_routes/route_set.rb +89 -0
  8. data/lib/action_dispatch/routing/direct_routes/route_set/custom_url_helper.rb +39 -0
  9. data/lib/action_dispatch/routing/direct_routes/route_set/named_route_collection.rb +36 -0
  10. data/lib/actionpack/direct_routes.rb +15 -0
  11. data/lib/actionpack/direct_routes/version.rb +5 -0
  12. data/test/application/routing_test.rb +206 -0
  13. data/test/dummy/README.rdoc +28 -0
  14. data/test/dummy/Rakefile +6 -0
  15. data/test/dummy/app/assets/javascripts/application.js +13 -0
  16. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  17. data/test/dummy/app/controllers/application_controller.rb +5 -0
  18. data/test/dummy/app/helpers/application_helper.rb +2 -0
  19. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  20. data/test/dummy/bin/bundle +3 -0
  21. data/test/dummy/bin/rails +4 -0
  22. data/test/dummy/bin/rake +4 -0
  23. data/test/dummy/bin/setup +29 -0
  24. data/test/dummy/config.ru +4 -0
  25. data/test/dummy/config/application.rb +26 -0
  26. data/test/dummy/config/boot.rb +5 -0
  27. data/test/dummy/config/database.yml +25 -0
  28. data/test/dummy/config/environment.rb +5 -0
  29. data/test/dummy/config/environments/development.rb +41 -0
  30. data/test/dummy/config/environments/production.rb +79 -0
  31. data/test/dummy/config/environments/test.rb +42 -0
  32. data/test/dummy/config/initializers/assets.rb +11 -0
  33. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  34. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  35. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  36. data/test/dummy/config/initializers/inflections.rb +16 -0
  37. data/test/dummy/config/initializers/mime_types.rb +4 -0
  38. data/test/dummy/config/initializers/session_store.rb +3 -0
  39. data/test/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  40. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  41. data/test/dummy/config/locales/en.yml +23 -0
  42. data/test/dummy/config/routes.rb +56 -0
  43. data/test/dummy/config/secrets.yml +22 -0
  44. data/test/dummy/db/test.sqlite3 +0 -0
  45. data/test/dummy/log/test.log +690 -0
  46. data/test/dummy/public/404.html +67 -0
  47. data/test/dummy/public/422.html +67 -0
  48. data/test/dummy/public/500.html +66 -0
  49. data/test/dummy/public/favicon.ico +0 -0
  50. data/test/integration/custom_url_helpers_test.rb +238 -0
  51. data/test/test_helper.rb +20 -0
  52. metadata +219 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a389d748b0ee8d8763f9c640a7b83442da5c8730
4
+ data.tar.gz: 9b6f8e1f13c5b8d62875d73fafff1cf6296f9031
5
+ SHA512:
6
+ metadata.gz: b8d4194c1a1ea9ecac75b08430c3e94c5b87d2a50c84566da824e07ac905ae62642ab134511dfc1a07b0ce864b0deee173e1f1bbf3a1f909085366af14eb970b
7
+ data.tar.gz: 99a148bae6858c94a4a7eb9c50bd2eab670ea0f3d7dff2588aac13bd37b2b2327fdb457aa4d49f8c299c036c18dfd7bb6b18f38e21c8dffdef43ba167502eec5
@@ -0,0 +1,20 @@
1
+ Copyright 2019 hamuyuuki
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
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # actionpack-direct_routes
2
+ `actionpack-direct_routes` backports Direct routes into Rails 4 and Rails 5.0.
3
+
4
+ Rails 5.1 adds Direct routes that you can create custom URL helpers directly.
5
+ [Ruby on Rails 5.1 Release Notes — Ruby on Rails Guides](https://guides.rubyonrails.org/5_1_release_notes.html#direct-resolved-routes)
6
+
7
+ ## Getting Started
8
+ Install `actionpack-direct_routes` at the command prompt:
9
+ ```
10
+ gem install actionpack-direct_routes
11
+ ```
12
+
13
+ Or add `actionpack-direct_routes` to your Gemfile:
14
+ ```
15
+ gem "actionpack-direct_routes"
16
+ ```
17
+
18
+ ## How to use
19
+ You can create custom URL helpers directly. For example:
20
+ ```
21
+ direct :homepage do
22
+ "http://www.rubyonrails.org"
23
+ end
24
+
25
+ # >> homepage_url
26
+ # => "http://www.rubyonrails.org"
27
+ ```
28
+
29
+ For details to [Rails Routing from the Outside In — Ruby on Rails Guides](https://guides.rubyonrails.org/routing.html#direct-routes)
30
+
31
+
32
+ ## Limitation
33
+ You can backport Direct routes into Rails 4.2 only now.
34
+
35
+ ## Contributing
36
+ Bug reports and pull requests are welcome on GitHub at https://github.com/hamuyuuki/actionpack-direct_routes. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
37
+
38
+ ## License
39
+ `actionpack-direct_routes` is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'DirectRoutes'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module Routing
5
+ module DirectRoutes
6
+ module Mapper
7
+ def direct(name, options = {}, &block)
8
+ unless @scope.root?
9
+ raise RuntimeError, "The direct method can't be used inside a routes scope block"
10
+ end
11
+
12
+ @set.add_url_helper(name, options, &block)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module Routing
5
+ module DirectRoutes
6
+ module Mapper
7
+ module Scope
8
+ def root?
9
+ @parent == {}
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module Routing
5
+ module DirectRoutes
6
+ module RouteSet
7
+ def add_url_helper(name, options, &block)
8
+ named_routes.add_url_helper(name, options, &block)
9
+ end
10
+
11
+ def url_helpers(supports_path = true)
12
+ routes = self
13
+
14
+ Module.new do
15
+ extend ActiveSupport::Concern
16
+ include ActionDispatch::Routing::UrlFor
17
+
18
+ # Define url_for in the singleton level so one can do:
19
+ # Rails.application.routes.url_helpers.url_for(args)
20
+ proxy_class = Class.new do
21
+ include ActionDispatch::Routing::UrlFor
22
+ include routes.named_routes.path_helpers_module
23
+ include routes.named_routes.url_helpers_module
24
+
25
+ attr_reader :_routes
26
+
27
+ def initialize(routes)
28
+ @_routes = routes
29
+ end
30
+
31
+ def optimize_routes_generation?
32
+ @_routes.optimize_routes_generation?
33
+ end
34
+ end
35
+
36
+ @_proxy = proxy_class.new(routes)
37
+
38
+ class << self
39
+ def url_for(options)
40
+ @_proxy.url_for(options)
41
+ end
42
+
43
+ def optimize_routes_generation?
44
+ @_proxy.optimize_routes_generation?
45
+ end
46
+
47
+ def _routes; @_proxy._routes; end
48
+ def url_options; {}; end
49
+ end
50
+
51
+ url_helpers = routes.named_routes.url_helpers_module
52
+
53
+ # Make named_routes available in the module singleton
54
+ # as well, so one can do:
55
+ # Rails.application.routes.url_helpers.posts_path
56
+ extend url_helpers
57
+
58
+ # Any class that includes this module will get all
59
+ # named routes...
60
+ include url_helpers
61
+
62
+ if supports_path
63
+ path_helpers = routes.named_routes.path_helpers_module
64
+
65
+ include path_helpers
66
+ extend path_helpers
67
+ end
68
+
69
+ # plus a singleton class method called _routes ...
70
+ included do
71
+ singleton_class.send(:redefine_method, :_routes) { routes }
72
+ end
73
+
74
+ # And an instance method _routes. Note that
75
+ # UrlFor (included in this module) add extra
76
+ # conveniences for working with @_routes.
77
+ define_method(:_routes) { @_routes || routes }
78
+
79
+ define_method(:_generate_paths_by_default) do
80
+ supports_path
81
+ end
82
+
83
+ private :_generate_paths_by_default
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module Routing
5
+ module DirectRoutes
6
+ module RouteSet
7
+ class CustomUrlHelper
8
+ attr_reader :name, :defaults, :block
9
+
10
+ def initialize(name, defaults, &block)
11
+ @name = name
12
+ @defaults = defaults
13
+ @block = block
14
+ end
15
+
16
+ def call(t, args, only_path = false)
17
+ options = args.extract_options!
18
+ url = t.url_for(eval_block(t, args, options))
19
+
20
+ if only_path
21
+ "/" + url.partition(%r{(?<!/)/(?!/)}).last
22
+ else
23
+ url
24
+ end
25
+ end
26
+
27
+ private
28
+ def eval_block(t, args, options)
29
+ t.instance_exec(*args, merge_defaults(options), &block)
30
+ end
31
+
32
+ def merge_defaults(options)
33
+ defaults ? defaults.merge(options) : options
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "custom_url_helper"
4
+
5
+ module ActionDispatch
6
+ module Routing
7
+ module DirectRoutes
8
+ module RouteSet
9
+ module NamedRouteCollection
10
+ def add_url_helper(name, defaults, &block)
11
+ helper = CustomUrlHelper.new(name, defaults, &block)
12
+ path_name = :"#{name}_path"
13
+ url_name = :"#{name}_url"
14
+
15
+ @path_helpers_module.module_eval do
16
+ redefine_method(path_name) do |*args|
17
+ helper.call(self, args, true)
18
+ end
19
+ end
20
+
21
+ @url_helpers_module.module_eval do
22
+ redefine_method(url_name) do |*args|
23
+ helper.call(self, args, false)
24
+ end
25
+ end
26
+
27
+ @path_helpers << path_name
28
+ @url_helpers << url_name
29
+
30
+ self
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/lazy_load_hooks"
4
+
5
+ ActiveSupport.on_load(:before_initialize) do
6
+ require "action_dispatch/routing/direct_routes/mapper"
7
+ require "action_dispatch/routing/direct_routes/mapper/scope"
8
+ require "action_dispatch/routing/direct_routes/route_set"
9
+ require "action_dispatch/routing/direct_routes/route_set/named_route_collection"
10
+
11
+ ActionDispatch::Routing::Mapper.include(ActionDispatch::Routing::DirectRoutes::Mapper)
12
+ ActionDispatch::Routing::Mapper::Scope.include(ActionDispatch::Routing::DirectRoutes::Mapper::Scope)
13
+ ActionDispatch::Routing::RouteSet.prepend(ActionDispatch::Routing::DirectRoutes::RouteSet)
14
+ ActionDispatch::Routing::RouteSet::NamedRouteCollection.include(ActionDispatch::Routing::DirectRoutes::RouteSet::NamedRouteCollection)
15
+ end
@@ -0,0 +1,5 @@
1
+ module ActionPack
2
+ module DirectRoutes
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,206 @@
1
+ require "active_support"
2
+ require "active_support/test_case"
3
+ require "active_support/testing/autorun"
4
+
5
+ require "action_controller"
6
+ require "action_controller/test_case"
7
+
8
+ require "rack/test"
9
+
10
+ class RoutingTest < ActiveSupport::TestCase
11
+ include ActiveSupport::Testing::Isolation
12
+ include Rack::Test::Methods
13
+
14
+ def app_path
15
+ @app_path ||= Dir.mktmpdir("direct_routes-")
16
+ end
17
+
18
+ def app_template_path
19
+ @app_template_path ||= File.join(File.expand_path("../", __dir__), "dummy")
20
+ end
21
+
22
+ def app_file(path, contents, mode = 'w')
23
+ FileUtils.mkdir_p File.dirname("#{app_path}/#{path}")
24
+ File.open("#{app_path}/#{path}", mode) do |f|
25
+ f.puts contents
26
+ end
27
+ end
28
+
29
+ def app(env = "production")
30
+ old_env = ENV["RAILS_ENV"]
31
+ @app ||= begin
32
+ ENV["RAILS_ENV"] = env
33
+ require "#{app_path}/config/environment"
34
+ Rails.application
35
+ end
36
+ ensure
37
+ ENV["RAILS_ENV"] = old_env
38
+ end
39
+
40
+ def controller(name, contents)
41
+ app_file("app/controllers/#{name}_controller.rb", contents)
42
+ end
43
+
44
+ def setup
45
+ FileUtils.cp_r(Dir.glob("#{app_template_path}/*"), app_path)
46
+ end
47
+
48
+ def teardown
49
+ FileUtils.rm_rf(app_path)
50
+ end
51
+
52
+ {
53
+ "development" => ["baz", "http://www.apple.com", "/dashboard"],
54
+ "production" => ["bar", "http://www.microsoft.com", "/profile"]
55
+ }.each do |mode, (expected_action, expected_url, expected_mapping)|
56
+ test "reloads routes when configuration is changed in #{mode}" do
57
+ controller :foo, <<-RUBY
58
+ class FooController < ApplicationController
59
+ def custom
60
+ render plain: custom_url
61
+ end
62
+ end
63
+ RUBY
64
+
65
+ app_file "config/routes.rb", <<-RUBY
66
+ Rails.application.routes.draw do
67
+ get 'custom', to: 'foo#custom'
68
+
69
+ direct(:custom) { "http://www.microsoft.com" }
70
+ end
71
+ RUBY
72
+
73
+ app(mode)
74
+
75
+ get "/custom"
76
+ assert_equal "http://www.microsoft.com", last_response.body
77
+
78
+ app_file "config/routes.rb", <<-RUBY
79
+ Rails.application.routes.draw do
80
+ get 'custom', to: 'foo#custom'
81
+
82
+ direct(:custom) { "http://www.apple.com" }
83
+ end
84
+ RUBY
85
+
86
+ sleep 0.1
87
+
88
+ get "/custom"
89
+ assert_equal expected_url, last_response.body
90
+ end
91
+ end
92
+
93
+ test "routes are added and removed when reloading" do
94
+ app("development")
95
+
96
+ controller :foo, <<-RUBY
97
+ class FooController < ApplicationController
98
+ def index
99
+ render plain: "foo"
100
+ end
101
+
102
+ def custom
103
+ render plain: custom_url
104
+ end
105
+ end
106
+ RUBY
107
+
108
+ controller :bar, <<-RUBY
109
+ class BarController < ApplicationController
110
+ def index
111
+ render plain: "bar"
112
+ end
113
+ end
114
+ RUBY
115
+
116
+ app_file "config/routes.rb", <<-RUBY
117
+ Rails.application.routes.draw do
118
+ get 'foo', to: 'foo#index'
119
+ end
120
+ RUBY
121
+
122
+ get "/foo"
123
+ assert_equal "foo", last_response.body
124
+ assert_equal "/foo", Rails.application.routes.url_helpers.foo_path
125
+
126
+ get "/bar"
127
+ assert_equal 404, last_response.status
128
+ assert_raises NoMethodError do
129
+ assert_equal "/bar", Rails.application.routes.url_helpers.bar_path
130
+ end
131
+
132
+ app_file "config/routes.rb", <<-RUBY
133
+ Rails.application.routes.draw do
134
+ get 'foo', to: 'foo#index'
135
+ get 'bar', to: 'bar#index'
136
+
137
+ get 'custom', to: 'foo#custom'
138
+ direct(:custom) { 'http://www.apple.com' }
139
+ end
140
+ RUBY
141
+
142
+ Rails.application.reload_routes!
143
+
144
+ get "/foo"
145
+ assert_equal "foo", last_response.body
146
+ assert_equal "/foo", Rails.application.routes.url_helpers.foo_path
147
+
148
+ get "/bar"
149
+ assert_equal "bar", last_response.body
150
+ assert_equal "/bar", Rails.application.routes.url_helpers.bar_path
151
+
152
+ get "/custom"
153
+ assert_equal "http://www.apple.com", last_response.body
154
+ assert_equal "http://www.apple.com", Rails.application.routes.url_helpers.custom_url
155
+
156
+ app_file "config/routes.rb", <<-RUBY
157
+ Rails.application.routes.draw do
158
+ get 'foo', to: 'foo#index'
159
+ end
160
+ RUBY
161
+
162
+ Rails.application.reload_routes!
163
+
164
+ get "/foo"
165
+ assert_equal "foo", last_response.body
166
+ assert_equal "/foo", Rails.application.routes.url_helpers.foo_path
167
+
168
+ get "/bar"
169
+ assert_equal 404, last_response.status
170
+ assert_raises NoMethodError do
171
+ assert_equal "/bar", Rails.application.routes.url_helpers.bar_path
172
+ end
173
+
174
+ get "/custom"
175
+ assert_equal 404, last_response.status
176
+ assert_raises NoMethodError do
177
+ assert_equal "http://www.apple.com", Rails.application.routes.url_helpers.custom_url
178
+ end
179
+ end
180
+
181
+ test "named routes are cleared when reloading" do
182
+ app_file "config/routes.rb", <<-RUBY
183
+ Rails.application.routes.draw do
184
+ direct(:microsoft) { 'http://www.microsoft.com' }
185
+ end
186
+ RUBY
187
+
188
+ app("development")
189
+
190
+ assert_equal "http://www.microsoft.com", Rails.application.routes.url_helpers.microsoft_url
191
+
192
+ app_file "config/routes.rb", <<-RUBY
193
+ Rails.application.routes.draw do
194
+ direct(:apple) { 'http://www.apple.com' }
195
+ end
196
+ RUBY
197
+
198
+ Rails.application.reload_routes!
199
+
200
+ assert_equal "http://www.apple.com", Rails.application.routes.url_helpers.apple_url
201
+
202
+ assert_raises NoMethodError do
203
+ assert_equal "http://www.microsoft.com", Rails.application.routes.url_helpers.microsoft_url
204
+ end
205
+ end
206
+ end