actionpack-direct_routes 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.
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