Module: CrudComponents::RouteResolver
- Defined in:
- lib/crud_components/route_resolver.rb
Overview
Resolves derived actions and record links to routes: the most specific conventional route first (association-scoped when the collection came from an association), then the top-level route, then nil — a nil means the button/link is omitted, never broken.
Class Method Summary collapse
- .action_path(view, action, record: nil, model: nil, owner: nil) ⇒ Object
-
.app_path(view, record) ⇒ Object
The host application's own page for a record — the same candidate logic, resolved against
main_appinstead of the current route set. -
.collection_index_path(view, target, owner, assoc_name) ⇒ Object
The index a has_many "+n more" link points at: 1.
- .collection_path(view, action, model, owner) ⇒ Object
-
.inverse_filter(target, owner, assoc_name) ⇒ Object
The target's belongs_to that mirrors the owner's collection (matched by foreign key), if it is filterable — with the owner's identify_by value.
- .member_candidates(prefix, record, owner) ⇒ Object
- .member_path(view, action, record, owner) ⇒ Object
- .missing_route_helper?(error) ⇒ Boolean
-
.record_path(view, record, owner: nil) ⇒ Object
The plain link to a record (label cells, association cells): show route, then edit.
-
.safe_block(view, subject, block) ⇒ Object
A path block naming a route helper that does not exist here — the same page rendered under a mount that lacks it — yields nil, so the button is omitted.
- .safe_url(view, helper, *args, **kwargs) ⇒ Object
-
.show_path(view, record, owner: nil) ⇒ Object
Whether the record has a plain (show) route — feeds the ":show button only without a label link" rule.
-
.try_helpers(helpers, candidates) ⇒ Object
helpersis anything exposing route helpers — the view, ormain_app.
Class Method Details
.action_path(view, action, record: nil, model: nil, owner: nil) ⇒ Object
9 10 11 12 13 14 15 16 17 |
# File 'lib/crud_components/route_resolver.rb', line 9 def action_path(view, action, record: nil, model: nil, owner: nil) return safe_block(view, record || model, action.path_block) if action.path_block if action.collection? collection_path(view, action, model, owner) else member_path(view, action, record, owner) end end |
.app_path(view, record) ⇒ Object
The host application's own page for a record — the same candidate logic,
resolved against main_app instead of the current route set. A declared
app_path block wins. nil when nothing resolves.
22 23 24 25 26 27 28 29 30 31 |
# File 'lib/crud_components/route_resolver.rb', line 22 def app_path(view, record) structure = Structure.for(record.class) if (block = structure.app_path_block) return safe_block(view, record, block) end return nil unless view.respond_to?(:main_app) try_helpers(view.main_app, member_candidates(nil, record, nil)) end |
.collection_index_path(view, target, owner, assoc_name) ⇒ Object
The index a has_many "+n more" link points at:
1. the nested index under the owner (publisher_books_path(publisher)),
2. else the target's index filtered by the owner — but ONLY when the
target actually has a filterable belongs_to back to the owner
(publisher→books works; a habtm like author↔books does not, so we
do not emit a link that would silently show everything),
3. else nil (the renderer shows "+n more" as plain text).
assoc_name is the owner's reflection name (e.g. :books).
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/crud_components/route_resolver.rb', line 57 def collection_index_path(view, target, owner, assoc_name) return nil unless owner key = target.model_name.route_key owner_key = owner.model_name.singular_route_key nested = "#{owner_key}_#{key}_path" return safe_url(view, nested, owner) if view.respond_to?(nested) flat = "#{key}_path" return nil unless view.respond_to?(flat) filter = inverse_filter(target, owner, assoc_name) return nil unless filter safe_url(view, flat, **{ filter[:param] => filter[:value] }) end |
.collection_path(view, action, model, owner) ⇒ Object
122 123 124 125 126 127 128 129 |
# File 'lib/crud_components/route_resolver.rb', line 122 def collection_path(view, action, model, owner) prefix = action.name == :new ? 'new_' : "#{action.name}_" key = action.name == :new ? model.model_name.singular_route_key : model.model_name.route_key candidates = [] candidates << ["#{prefix}#{owner.model_name.singular_route_key}_#{key}_path", [owner]] if owner candidates << ["#{prefix}#{key}_path", []] try_helpers(view, candidates) end |
.inverse_filter(target, owner, assoc_name) ⇒ Object
The target's belongs_to that mirrors the owner's collection (matched by foreign key), if it is filterable — with the owner's identify_by value.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/crud_components/route_resolver.rb', line 76 def inverse_filter(target, owner, assoc_name) owner_reflection = owner.class.reflect_on_association(assoc_name) return nil unless owner_reflection&.foreign_key fk = owner_reflection.foreign_key.to_s inverse = target.reflect_on_all_associations(:belongs_to).find { |r| r.foreign_key.to_s == fk } return nil unless inverse field = Structure.for(target).field(inverse.name) return nil unless field.filterable? identify = Structure.for(owner.class).identify_by { param: inverse.name, value: owner.public_send(identify) } rescue CrudComponents::DefinitionError nil end |
.member_candidates(prefix, record, owner) ⇒ Object
131 132 133 134 135 136 137 |
# File 'lib/crud_components/route_resolver.rb', line 131 def member_candidates(prefix, record, owner) key = record.model_name.singular_route_key candidates = [] candidates << ["#{prefix}#{owner.model_name.singular_route_key}_#{key}_path", [owner, record]] if owner candidates << ["#{prefix}#{key}_path", [record]] candidates end |
.member_path(view, action, record, owner) ⇒ Object
117 118 119 120 |
# File 'lib/crud_components/route_resolver.rb', line 117 def member_path(view, action, record, owner) prefix = { show: nil, destroy: nil, edit: 'edit_' }.fetch(action.name, "#{action.name}_") try_helpers(view, member_candidates(prefix, record, owner)) end |
.missing_route_helper?(error) ⇒ Boolean
107 108 109 |
# File 'lib/crud_components/route_resolver.rb', line 107 def missing_route_helper?(error) error.name.to_s.end_with?('_path', '_url') end |
.record_path(view, record, owner: nil) ⇒ Object
The plain link to a record (label cells, association cells): show route, then edit. Returns [path, kind] or nil.
35 36 37 38 39 40 41 |
# File 'lib/crud_components/route_resolver.rb', line 35 def record_path(view, record, owner: nil) path = try_helpers(view, member_candidates(nil, record, owner)) return [path, :show] if path path = try_helpers(view, member_candidates('edit_', record, owner)) path ? [path, :edit] : nil end |
.safe_block(view, subject, block) ⇒ Object
A path block naming a route helper that does not exist here — the same page rendered under a mount that lacks it — yields nil, so the button is omitted. Anything else the block gets wrong is the app's own error and stays loud.
97 98 99 100 101 102 103 104 105 |
# File 'lib/crud_components/route_resolver.rb', line 97 def safe_block(view, subject, block) view.instance_exec(subject, &block) rescue ActionController::UrlGenerationError nil rescue NameError => e raise unless missing_route_helper?(e) nil end |
.safe_url(view, helper, *args, **kwargs) ⇒ Object
111 112 113 114 115 |
# File 'lib/crud_components/route_resolver.rb', line 111 def safe_url(view, helper, *args, **kwargs) kwargs.empty? ? view.public_send(helper, *args) : view.public_send(helper, *args, **kwargs) rescue ActionController::UrlGenerationError, NoMethodError nil end |
.show_path(view, record, owner: nil) ⇒ Object
Whether the record has a plain (show) route — feeds the ":show button only without a label link" rule.
45 46 47 |
# File 'lib/crud_components/route_resolver.rb', line 45 def show_path(view, record, owner: nil) try_helpers(view, member_candidates(nil, record, owner)) end |
.try_helpers(helpers, candidates) ⇒ Object
helpers is anything exposing route helpers — the view, or main_app.
140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/crud_components/route_resolver.rb', line 140 def try_helpers(helpers, candidates) candidates.each do |helper, args| next unless helpers.respond_to?(helper) begin return helpers.public_send(helper, *args) rescue ActionController::UrlGenerationError, NoMethodError next end end nil end |