pakyow-reflection 1.0.3
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE +4 -0
- data/README.md +29 -0
- data/lib/pakyow/application/behavior/reflection/reflecting.rb +54 -0
- data/lib/pakyow/application/config/reflection.rb +33 -0
- data/lib/pakyow/presenter/renderer/behavior/reflection/install_form_metadata.rb +30 -0
- data/lib/pakyow/reflection.rb +12 -0
- data/lib/pakyow/reflection/action.rb +30 -0
- data/lib/pakyow/reflection/attribute.rb +30 -0
- data/lib/pakyow/reflection/builders/actions.rb +81 -0
- data/lib/pakyow/reflection/builders/base.rb +13 -0
- data/lib/pakyow/reflection/builders/endpoints.rb +87 -0
- data/lib/pakyow/reflection/builders/helpers/controller.rb +234 -0
- data/lib/pakyow/reflection/builders/source.rb +60 -0
- data/lib/pakyow/reflection/endpoint.rb +84 -0
- data/lib/pakyow/reflection/extensions/controller.rb +314 -0
- data/lib/pakyow/reflection/framework.rb +67 -0
- data/lib/pakyow/reflection/mirror.rb +350 -0
- data/lib/pakyow/reflection/nested.rb +30 -0
- data/lib/pakyow/reflection/scope.rb +67 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d9e1be2fff54bd5e80f3768b5b5dd97807f1487b6d59fd7815cde828d9d17ed7
|
4
|
+
data.tar.gz: c7210f0d4c895b4f1148a7a78ed3f6ae13bac65608bcdfce2cca997babbb6d0a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d48551b8e337febffedfa1415d11f0e695c9510e784ea8b802cb16e1868db51ca5470c6485a9081776cce0f6944cef8862cb57ee6e0868270dbddf2ebd063db8
|
7
|
+
data.tar.gz: 89f0632f6218b457a13e6401615a7df6d45faf58fc3c03080526ff3db2599492111eff260c7b87163f476a69e9502f44d3a1ce533e1f17b248c757b671a6b61d
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# pakyow-reflection
|
2
|
+
|
3
|
+
Reflected behavior for Pakyow.
|
4
|
+
|
5
|
+
# Download
|
6
|
+
|
7
|
+
The latest version of Pakyow Reflection can be installed with RubyGems:
|
8
|
+
|
9
|
+
```
|
10
|
+
gem install pakyow-reflection
|
11
|
+
```
|
12
|
+
|
13
|
+
Source code can be downloaded as part of the Pakyow project on Github:
|
14
|
+
|
15
|
+
- https://github.com/pakyow/pakyow/tree/master/pakyow-reflection
|
16
|
+
|
17
|
+
# License
|
18
|
+
|
19
|
+
Pakyow Reflection is free and open-source under the [LGPLv3 license](https://choosealicense.com/licenses/gpl-3.0/).
|
20
|
+
|
21
|
+
# Support
|
22
|
+
|
23
|
+
Found a bug? Tell us about it here:
|
24
|
+
|
25
|
+
- https://github.com/pakyow/pakyow/issues
|
26
|
+
|
27
|
+
We'd love to have you in the community:
|
28
|
+
|
29
|
+
- http://pakyow.org/get-involved
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/class_state"
|
4
|
+
|
5
|
+
require "pakyow/reflection/mirror"
|
6
|
+
|
7
|
+
module Pakyow
|
8
|
+
class Application
|
9
|
+
module Behavior
|
10
|
+
module Reflection
|
11
|
+
module Reflecting
|
12
|
+
extend Support::Extension
|
13
|
+
|
14
|
+
apply_extension do
|
15
|
+
attr_reader :mirror
|
16
|
+
|
17
|
+
after "initialize", priority: :high do
|
18
|
+
@mirror = Pakyow::Reflection::Mirror.new(self)
|
19
|
+
|
20
|
+
builders = Hash[
|
21
|
+
config.reflection.builders.map { |type, builder|
|
22
|
+
[type, builder.new(self, @mirror.scopes)]
|
23
|
+
}
|
24
|
+
]
|
25
|
+
|
26
|
+
# Build the scopes.
|
27
|
+
#
|
28
|
+
@mirror.scopes.each do |scope|
|
29
|
+
builders[:source].build(scope)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Build the actions.
|
33
|
+
#
|
34
|
+
@mirror.scopes.each do |scope|
|
35
|
+
builders[:actions].build(scope.actions)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Build the endpoints.
|
39
|
+
#
|
40
|
+
builders[:endpoints].build(@mirror.endpoints)
|
41
|
+
|
42
|
+
# Cleanup.
|
43
|
+
#
|
44
|
+
unless Pakyow.env?(:test)
|
45
|
+
@mirror.scopes.each(&:cleanup)
|
46
|
+
@mirror.endpoints.each(&:cleanup)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/class_state"
|
4
|
+
|
5
|
+
require "pakyow/reflection/builders/source"
|
6
|
+
require "pakyow/reflection/builders/endpoints"
|
7
|
+
require "pakyow/reflection/builders/actions"
|
8
|
+
|
9
|
+
module Pakyow
|
10
|
+
class Application
|
11
|
+
module Config
|
12
|
+
module Reflection
|
13
|
+
extend Support::Extension
|
14
|
+
|
15
|
+
apply_extension do
|
16
|
+
configurable :reflection do
|
17
|
+
setting :builders, {
|
18
|
+
source: Pakyow::Reflection::Builders::Source,
|
19
|
+
endpoints: Pakyow::Reflection::Builders::Endpoints,
|
20
|
+
actions: Pakyow::Reflection::Builders::Actions
|
21
|
+
}
|
22
|
+
|
23
|
+
setting :ignored_template_stores, [:errors]
|
24
|
+
|
25
|
+
configurable :data do
|
26
|
+
setting :connection, :default
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/extension"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module Presenter
|
7
|
+
class Renderer
|
8
|
+
module Behavior
|
9
|
+
module Reflection
|
10
|
+
module InstallFormMetadata
|
11
|
+
extend Support::Extension
|
12
|
+
|
13
|
+
apply_extension do
|
14
|
+
build do |view, composer:|
|
15
|
+
forms = view.forms
|
16
|
+
if !view.object.is_a?(StringDoc) && view.object.significant?(:form)
|
17
|
+
forms << view
|
18
|
+
end
|
19
|
+
|
20
|
+
forms.each do |form|
|
21
|
+
form.label(:form)[:view_path] = composer.view_path
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/routing"
|
4
|
+
require "pakyow/presenter"
|
5
|
+
|
6
|
+
# Load data after presenter, so that containers are created with reflected attributes.
|
7
|
+
#
|
8
|
+
require "pakyow/data"
|
9
|
+
|
10
|
+
require "pakyow/form"
|
11
|
+
|
12
|
+
require "pakyow/reflection/framework"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/inflector"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module Reflection
|
7
|
+
# @api private
|
8
|
+
class Action
|
9
|
+
attr_reader :name, :scope, :node, :view_path, :binding, :attributes, :nested, :parents
|
10
|
+
|
11
|
+
def initialize(name:, scope:, node:, view_path:, binding: nil, attributes: [], nested: [], parents: [])
|
12
|
+
@name, @scope, @node, @view_path, @binding, @attributes, @nested, @parents = normalize(name), scope, node, view_path, binding, attributes, nested, parents
|
13
|
+
end
|
14
|
+
|
15
|
+
def named?(name)
|
16
|
+
@name == normalize(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def cleanup
|
20
|
+
@node = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def normalize(name)
|
26
|
+
Support.inflector.singularize(name.to_s).to_sym
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/inflector"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module Reflection
|
7
|
+
# @api private
|
8
|
+
class Attribute
|
9
|
+
attr_reader :name, :type
|
10
|
+
|
11
|
+
def initialize(name, type:, required: false)
|
12
|
+
@name, @type, @required = normalize(name), type, required
|
13
|
+
end
|
14
|
+
|
15
|
+
def named?(name)
|
16
|
+
@name == normalize(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def required?
|
20
|
+
@required == true
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def normalize(name)
|
26
|
+
name.to_s.to_sym
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/reflection/builders/base"
|
4
|
+
require "pakyow/reflection/builders/helpers/controller"
|
5
|
+
|
6
|
+
module Pakyow
|
7
|
+
module Reflection
|
8
|
+
module Builders
|
9
|
+
# @api private
|
10
|
+
class Actions < Base
|
11
|
+
include Helpers::Controller
|
12
|
+
|
13
|
+
def build(actions)
|
14
|
+
actions.each do |action|
|
15
|
+
define_action(action)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def define_action(action)
|
22
|
+
if action.parents.any?
|
23
|
+
parents = action.parents.dup
|
24
|
+
|
25
|
+
current_resource = ensure_controller_has_helpers(
|
26
|
+
find_or_define_resource_for_scope_at_path(parents.shift, action.view_path)
|
27
|
+
)
|
28
|
+
|
29
|
+
parents.each do |parent|
|
30
|
+
current_resource = ensure_controller_has_helpers(
|
31
|
+
find_or_define_resource_for_scope_in_resource(parent, current_resource)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
resource = find_or_define_resource_for_scope_in_resource(action.scope, current_resource)
|
36
|
+
else
|
37
|
+
resource = find_or_define_resource_for_scope_at_path(action.scope, action.view_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
ensure_controller_has_helpers(resource)
|
41
|
+
|
42
|
+
# Define the route unless it exists.
|
43
|
+
#
|
44
|
+
# Actions are easy since they always go in the resource controller for
|
45
|
+
# the scope. If a nested scope, the action is defined on the nested
|
46
|
+
# resource returned by `find_or_define_resource_for_scope`.
|
47
|
+
#
|
48
|
+
route = resource.routes.values.flatten.find { |possible_route|
|
49
|
+
possible_route.name == action.name
|
50
|
+
} || resource.send(action.name) do
|
51
|
+
reflect
|
52
|
+
end
|
53
|
+
|
54
|
+
# Install the reflect action if it hasn't been installed for this route.
|
55
|
+
#
|
56
|
+
if route.name
|
57
|
+
unless action.node.labeled?(:endpoint)
|
58
|
+
form_endpoint_name = [resource.name_of_self.to_s, route.name.to_s].join("_").to_sym
|
59
|
+
action.node.significance << :endpoint
|
60
|
+
action.node.set_label(:endpoint, form_endpoint_name)
|
61
|
+
action.node.attributes[:"data-e"] = form_endpoint_name
|
62
|
+
end
|
63
|
+
|
64
|
+
resource.action :set_reflected_action, only: [route.name] do
|
65
|
+
if connection.form
|
66
|
+
form_view_path = connection.form[:view_path]
|
67
|
+
form_binding = connection.form[:binding]&.to_sym
|
68
|
+
|
69
|
+
connection.set(:__reflected_action, action.scope.actions.find { |possible_action|
|
70
|
+
possible_action.view_path == form_view_path && possible_action.binding == form_binding
|
71
|
+
})
|
72
|
+
end
|
73
|
+
end
|
74
|
+
else
|
75
|
+
# TODO: warn the user that a reflection couldn't be installed for an unnamed route
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/core_refinements/string/normalization"
|
4
|
+
|
5
|
+
require "pakyow/reflection/builders/base"
|
6
|
+
require "pakyow/reflection/builders/helpers/controller"
|
7
|
+
require "pakyow/reflection/extensions/controller"
|
8
|
+
|
9
|
+
module Pakyow
|
10
|
+
module Reflection
|
11
|
+
module Builders
|
12
|
+
# @api private
|
13
|
+
class Endpoints < Base
|
14
|
+
include Helpers::Controller
|
15
|
+
|
16
|
+
using Support::Refinements::String::Normalization
|
17
|
+
|
18
|
+
def build(endpoints)
|
19
|
+
endpoints.each do |endpoint|
|
20
|
+
define_endpoint(endpoint)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def define_endpoint(endpoint)
|
27
|
+
controller = if within_resource?(endpoint.view_path)
|
28
|
+
find_or_define_resource_for_scope_at_path(
|
29
|
+
resource_source_at_path(endpoint.view_path),
|
30
|
+
controller_path(endpoint.view_path),
|
31
|
+
endpoint.type
|
32
|
+
)
|
33
|
+
else
|
34
|
+
find_or_define_controller_at_path(controller_path(endpoint.view_path))
|
35
|
+
end
|
36
|
+
|
37
|
+
# TODO: Make this the responsibility of the helpers.
|
38
|
+
#
|
39
|
+
ensure_controller_has_helpers(controller)
|
40
|
+
|
41
|
+
if controller.expansions.include?(:resource)
|
42
|
+
endpoint_name = String.normalize_path(
|
43
|
+
endpoint.view_path.gsub(String.collapse_path(controller.path_to_self), "")
|
44
|
+
).split("/", 2)[1]
|
45
|
+
|
46
|
+
endpoint_name = if endpoint_name.empty?
|
47
|
+
:list
|
48
|
+
else
|
49
|
+
endpoint_name.to_sym
|
50
|
+
end
|
51
|
+
|
52
|
+
case endpoint_name
|
53
|
+
when :new, :edit, :list, :show
|
54
|
+
# Find or define the route by resource endpoint name.
|
55
|
+
#
|
56
|
+
route = controller.routes.values.flatten.find { |possible_route|
|
57
|
+
possible_route.name == endpoint_name
|
58
|
+
} || controller.send(endpoint_name) do
|
59
|
+
reflect
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
unless route
|
65
|
+
# Find or define the route by path.
|
66
|
+
#
|
67
|
+
# TODO: This should look across all controllers, not just the current one. Look through endpoints?
|
68
|
+
#
|
69
|
+
route = controller.routes.values.flatten.find { |possible_route|
|
70
|
+
possible_route.path == route_path(endpoint.view_path)
|
71
|
+
} || controller.get(route_name(endpoint.view_path), route_path(endpoint.view_path)) do
|
72
|
+
operations.reflect(controller: self)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
if route.name
|
77
|
+
controller.action :set_reflected_endpoint, only: [route.name] do
|
78
|
+
connection.set(:__reflected_endpoint, endpoint)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
# TODO: warn the user that a reflection couldn't be installed for an unnamed route
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/core_refinements/string/normalization"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module Reflection
|
7
|
+
module Builders
|
8
|
+
module Helpers
|
9
|
+
module Controller
|
10
|
+
using Support::Refinements::String::Normalization
|
11
|
+
|
12
|
+
def find_or_define_controller_at_path(path)
|
13
|
+
controller_at_path(path) || define_controller_at_path(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def controller_at_path(path, state = @app.state(:controller))
|
17
|
+
if state.any?
|
18
|
+
state.find { |controller|
|
19
|
+
String.normalize_path(String.collapse_path(controller.path_to_self)) == String.normalize_path(path)
|
20
|
+
} || controller_at_path(path, state.flat_map(&:children))
|
21
|
+
else
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def controller_closest_to_path(path, state = @app.state(:controller))
|
27
|
+
if state.any?
|
28
|
+
controller_closest_to_path(path, state.flat_map(&:children)) || state.find { |controller|
|
29
|
+
String.normalize_path(path).start_with?(String.normalize_path(String.collapse_path(controller.path_to_self)))
|
30
|
+
}
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def define_controller_at_path(path, within: nil)
|
37
|
+
nested_state = if within
|
38
|
+
within.children
|
39
|
+
else
|
40
|
+
@app.state(:controller)
|
41
|
+
end
|
42
|
+
|
43
|
+
path = String.normalize_path(path)
|
44
|
+
|
45
|
+
if controller = controller_closest_to_path(path, nested_state)
|
46
|
+
context = controller
|
47
|
+
path = path.gsub(
|
48
|
+
/^#{String.normalize_path(String.collapse_path(controller.path_to_self))}/, ""
|
49
|
+
)
|
50
|
+
else
|
51
|
+
context = within || @app
|
52
|
+
end
|
53
|
+
|
54
|
+
controller_name = if path == "/"
|
55
|
+
:root
|
56
|
+
else
|
57
|
+
String.normalize_path(path)[1..-1].gsub("/", "_").to_sym
|
58
|
+
end
|
59
|
+
|
60
|
+
method = if context.is_a?(Class) && context.ancestors.include?(Pakyow::Routing::Controller)
|
61
|
+
:namespace
|
62
|
+
else
|
63
|
+
:controller
|
64
|
+
end
|
65
|
+
|
66
|
+
context.send(method, controller_name, String.normalize_path(path)) do
|
67
|
+
# intentionally empty
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def controller_path(view_path)
|
72
|
+
if view_path_directory?(view_path)
|
73
|
+
view_path
|
74
|
+
else
|
75
|
+
view_path.split("/")[0..-2].to_a.join("/")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def within_resource?(view_path)
|
80
|
+
view_path.split("/").any? { |view_path_part|
|
81
|
+
@app.state(:source).any? { |source|
|
82
|
+
source.plural_name == view_path_part.to_sym
|
83
|
+
}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def route_name(view_path)
|
88
|
+
if view_path_directory?(view_path)
|
89
|
+
:default
|
90
|
+
else
|
91
|
+
view_path.split("/").last.to_sym
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def route_path(view_path)
|
96
|
+
if view_path_directory?(view_path)
|
97
|
+
"/"
|
98
|
+
else
|
99
|
+
"/#{view_path.split("/").last}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def resource_source_at_path(view_path)
|
104
|
+
view_path.split("/").reverse.each do |view_path_part|
|
105
|
+
@app.state(:source).each do |source|
|
106
|
+
if source.plural_name == view_path_part.to_sym
|
107
|
+
return source
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def view_path_directory?(view_path)
|
114
|
+
@app.state(:templates).any? { |templates|
|
115
|
+
File.directory?(File.join(templates.path, templates.config[:paths][:pages], view_path))
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
RESOURCE_ENDPOINTS = %i(new edit list show).freeze
|
120
|
+
|
121
|
+
def find_or_define_resource_for_scope_at_path(scope, path, endpoint_type = nil)
|
122
|
+
resource = resource_for_scope_at_path(scope, path) || define_resource_for_scope_at_path(scope, path)
|
123
|
+
|
124
|
+
if path.end_with?(resource_path_for_scope(scope)) || endpoint_type.nil? || RESOURCE_ENDPOINTS.include?(path.split("/").last.to_sym)
|
125
|
+
return resource
|
126
|
+
else
|
127
|
+
controller_for_endpoint_type = resource.send(endpoint_type)
|
128
|
+
|
129
|
+
nested_path = if view_path_directory?(path)
|
130
|
+
path
|
131
|
+
else
|
132
|
+
path.split("/")[0..-2].join("/")
|
133
|
+
end
|
134
|
+
|
135
|
+
nested_path = nested_path.gsub(
|
136
|
+
/^#{String.normalize_path(String.collapse_path(controller_for_endpoint_type.path_to_self))}/, ""
|
137
|
+
)
|
138
|
+
|
139
|
+
if current_controller = controller_at_path(nested_path, resource.children)
|
140
|
+
return current_controller
|
141
|
+
else
|
142
|
+
if nested_path.empty?
|
143
|
+
controller_for_endpoint_type
|
144
|
+
else
|
145
|
+
define_controller_at_path(nested_path, within: controller_for_endpoint_type)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def resource_for_scope_at_path(scope, path, state = @app.state(:controller))
|
152
|
+
if state.any?
|
153
|
+
state.select { |controller|
|
154
|
+
controller.expansions.include?(:resource)
|
155
|
+
}.find { |controller|
|
156
|
+
String.normalize_path(String.collapse_path(controller.path_to_self)) == full_resource_path_for_scope_at_path(scope, path)
|
157
|
+
} || resource_for_scope_at_path(scope, path, state.flat_map(&:children))
|
158
|
+
else
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def define_resource_for_scope_at_path(scope, path)
|
164
|
+
context = if resource_namespace_path = resource_namespace_path_for_scope_at_path(scope, path)
|
165
|
+
if within_resource?(resource_namespace_path)
|
166
|
+
ensure_controller_has_helpers(
|
167
|
+
find_or_define_resource_for_scope_at_path(
|
168
|
+
resource_source_at_path(resource_namespace_path), resource_namespace_path
|
169
|
+
)
|
170
|
+
)
|
171
|
+
else
|
172
|
+
ensure_controller_has_helpers(
|
173
|
+
find_or_define_controller_at_path(resource_namespace_path)
|
174
|
+
)
|
175
|
+
end
|
176
|
+
else
|
177
|
+
@app
|
178
|
+
end
|
179
|
+
|
180
|
+
context.resource resource_name_for_scope(scope), resource_path_for_scope(scope) do
|
181
|
+
# intentionally empty
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def resource_namespace_path_for_scope_at_path(scope, path)
|
186
|
+
resource_path = resource_path_for_scope(scope)
|
187
|
+
|
188
|
+
if path.start_with?(resource_path)
|
189
|
+
nil
|
190
|
+
elsif path.include?(resource_path)
|
191
|
+
path.split(resource_path, 2)[0]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def resource_name_for_scope(scope)
|
196
|
+
scope.plural_name
|
197
|
+
end
|
198
|
+
|
199
|
+
def resource_path_for_scope(scope)
|
200
|
+
String.normalize_path(scope.plural_name)
|
201
|
+
end
|
202
|
+
|
203
|
+
def ensure_controller_has_helpers(controller)
|
204
|
+
unless controller.ancestors.include?(Extension::Controller)
|
205
|
+
controller.include Extension::Controller
|
206
|
+
end
|
207
|
+
|
208
|
+
controller
|
209
|
+
end
|
210
|
+
|
211
|
+
def find_or_define_resource_for_scope_in_resource(scope, resource)
|
212
|
+
resource.children.find { |child|
|
213
|
+
child.path == resource_path_for_scope(scope)
|
214
|
+
} || resource.resource(resource_name_for_scope(scope), resource_path_for_scope(scope)) do
|
215
|
+
# intentionally empty
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def full_resource_path_for_scope_at_path(scope, path)
|
220
|
+
String.normalize_path(
|
221
|
+
File.join(
|
222
|
+
resource_namespace_path_for_scope_at_path(scope, path).to_s,
|
223
|
+
scope.plural_name.to_s
|
224
|
+
)
|
225
|
+
)
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|