wayfinding 0.0.1
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/Rakefile +11 -0
- data/docs/README.md +137 -0
- data/lib/wayfinding/destination.rb +67 -0
- data/lib/wayfinding/destination_list.rb +14 -0
- data/lib/wayfinding/errors.rb +15 -0
- data/lib/wayfinding/kind.rb +29 -0
- data/lib/wayfinding/rspec.rb +33 -0
- data/lib/wayfinding/url_resolver_context.rb +25 -0
- data/lib/wayfinding/version.rb +5 -0
- data/lib/wayfinding.rb +119 -0
- metadata +192 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ad2f192ae238999788d4d63f9ae53e60f5897f76ea841c4d6a44292b181a7f5c
|
|
4
|
+
data.tar.gz: acd21e66e0788c34ed59b4e4f83239fa9d4f8a3004756b0d5bb7d2c0584c9ee0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: cc7e58f174db34dd517d2e7aa7f1f90797f6de157ac0a2b8bce3a26d000ff9abf52358b0c27acb7fe1c6eccc55dd93d22515133d0cfc12fb50d20c18e85fbe02
|
|
7
|
+
data.tar.gz: 49161506abc7526fe2276ad34b329e0828a8c5f873b042590c1c2ddd3b91833138e61d8717814a6a012ab73f35f02340a74aa42f89d379182af64822336dcddb
|
data/Rakefile
ADDED
data/docs/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Wayfinding
|
|
2
|
+
|
|
3
|
+
Cross-application URL resolution.
|
|
4
|
+
|
|
5
|
+
Wayfinding is a global registry of named destinations. It lets one component link to a route owned by
|
|
6
|
+
another without depending on that component.
|
|
7
|
+
|
|
8
|
+
Links are not functionality. No component should acquire a dependency because it renders an anchor tag.
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
Without a registry, a component that links to another component's page must reach into that engine's
|
|
13
|
+
`url_helpers`, depend on the owner just for the link, or hardcode the path. Each option couples the caller
|
|
14
|
+
to the route's current owner or location; a hardcoded path can also break silently.
|
|
15
|
+
|
|
16
|
+
Wayfinding gives one accessor and one registration. Moving a route becomes a one-line change to the
|
|
17
|
+
registration; every call site is untouched.
|
|
18
|
+
|
|
19
|
+
## Registering
|
|
20
|
+
|
|
21
|
+
Registration is configuration: every argument is a keyword, so order does not matter. Register from an
|
|
22
|
+
initializer.
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
Wayfinding.register(
|
|
26
|
+
name: :home,
|
|
27
|
+
engine: -> { Homes::Engine },
|
|
28
|
+
helper: :home_path
|
|
29
|
+
)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`engine:` accepts a lambda or the constant. Prefer the lambda so the engine is resolved lazily.
|
|
33
|
+
|
|
34
|
+
`helper:` is the common form. Wayfinding derives the corresponding `_url` helper for `url_for`.
|
|
35
|
+
|
|
36
|
+
For anything that is not a bare helper call, pass a block. Within the block, `self` is the engine's
|
|
37
|
+
`url_helpers` object. That lets the resolver call `results_path(...)` directly instead of repeating
|
|
38
|
+
`Search::Engine.routes.url_helpers.results_path(...)`.
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
Wayfinding.register(name: :search_results, engine: -> { Search::Engine }) do |query|
|
|
42
|
+
results_path(q: query)
|
|
43
|
+
end
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For `url_for`, calls ending in `_path` inside an engine-backed block are mapped to the corresponding
|
|
47
|
+
`_url` helper. Wayfinding cannot convert an arbitrary path string after the block returns, so a block
|
|
48
|
+
that supports both lookup forms must build its result from route helpers rather than hardcoding a path.
|
|
49
|
+
|
|
50
|
+
Omit `engine:` for a destination that is not a Rails route. Wayfinding calls the block directly, and
|
|
51
|
+
`path_for` and `url_for` both return its result.
|
|
52
|
+
|
|
53
|
+
## Looking up
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
Wayfinding.path_for(:home, home, tab: "activity")
|
|
57
|
+
# => "/homes/17?tab=activity"
|
|
58
|
+
|
|
59
|
+
Wayfinding.url_for(:home, home)
|
|
60
|
+
# => "https://example.com/homes/17"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Looking up an unregistered destination raises `Wayfinding::UnregisteredDestination`, listing what is
|
|
64
|
+
registered. There is no null object and no production fallback: a missing registration is a boot-state
|
|
65
|
+
bug, not a runtime condition.
|
|
66
|
+
|
|
67
|
+
## Kinds
|
|
68
|
+
|
|
69
|
+
A kind is a named field contract. Applications define kinds; Wayfinding only enforces them.
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
Wayfinding.define_kind(:report, requires: %i[label description action subject])
|
|
73
|
+
|
|
74
|
+
Wayfinding.register(
|
|
75
|
+
name: :activity_report,
|
|
76
|
+
kind: :report,
|
|
77
|
+
engine: -> { Reports::Engine },
|
|
78
|
+
helper: :activity_reports_path,
|
|
79
|
+
action: :view_activity_report,
|
|
80
|
+
subject: -> { Account },
|
|
81
|
+
label: "Activity Report",
|
|
82
|
+
description: "Summarizes recent account activity"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
Wayfinding.of_kind(:report).accessible_by(current_ability)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A destination without a kind is still resolvable by `path_for`, but is invisible to `of_kind`. Declaring
|
|
89
|
+
a kind does not remove point lookup, so `path_for(:activity_report)` still works.
|
|
90
|
+
|
|
91
|
+
`requires:` may name any field. Validation asserts presence only and never resolves callables, so a
|
|
92
|
+
lazily registered `subject: -> { Account }` passes without autoloading the model.
|
|
93
|
+
|
|
94
|
+
### Destination metadata
|
|
95
|
+
|
|
96
|
+
Every keyword other than the routing structure is application metadata. Wayfinding does not give fields
|
|
97
|
+
such as `label` or `description` special behavior. Metadata is stored without interpretation and read
|
|
98
|
+
with `#[]`:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
destination = Wayfinding.fetch(:activity_report)
|
|
102
|
+
destination[:label]
|
|
103
|
+
destination[:data]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Resolve any callable field lazily with `#value_for`:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
destination.value_for(:subject)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`action` and `subject` are metadata with one additional invariant: if either is supplied, both must be.
|
|
113
|
+
`accessible_by(ability)` reads that pair and calls `ability.can?(action, subject)`. Display policy, such as
|
|
114
|
+
falling back from a missing description to a label, belongs to the consuming application.
|
|
115
|
+
|
|
116
|
+
## Verifying
|
|
117
|
+
|
|
118
|
+
Assert at boot that every expected destination is registered, so a dropped registration fails the boot
|
|
119
|
+
rather than rendering a broken link.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
Wayfinding.verify!(%i[home activity_report])
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Testing
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
require "wayfinding/rspec"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Each example runs inside `Wayfinding.preserve!`, so anything registered in a spec is rolled back. Use
|
|
132
|
+
`stub_destination` rather than mocking Wayfinding itself.
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
stub_destination(:home, "/homes/1")
|
|
136
|
+
stub_destination(:home) { |home| "/homes/#{home.id}" }
|
|
137
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wayfinding
|
|
4
|
+
# A named, engine-scoped URL resolver with arbitrary application metadata.
|
|
5
|
+
# Metadata is stored without interpretation and read through #[]. Any field
|
|
6
|
+
# can be resolved lazily through #value_for.
|
|
7
|
+
class Destination
|
|
8
|
+
attr_reader :name, :kind, :attributes
|
|
9
|
+
|
|
10
|
+
# rubocop:disable Metrics/ParameterLists
|
|
11
|
+
def initialize(name:, engine: nil, kind: nil, helper: nil, resolver: nil, **attributes)
|
|
12
|
+
@name = name.to_sym
|
|
13
|
+
@engine = engine
|
|
14
|
+
@kind = kind&.to_sym
|
|
15
|
+
@helper = helper&.to_sym
|
|
16
|
+
@resolver = resolver
|
|
17
|
+
@attributes = attributes.freeze
|
|
18
|
+
freeze
|
|
19
|
+
end
|
|
20
|
+
# rubocop:enable Metrics/ParameterLists
|
|
21
|
+
|
|
22
|
+
def action = self[:action]
|
|
23
|
+
|
|
24
|
+
def subject = value_for(:subject)
|
|
25
|
+
|
|
26
|
+
def [](key) = attributes[key.to_sym]
|
|
27
|
+
|
|
28
|
+
def value_for(key) = resolve(self[key])
|
|
29
|
+
|
|
30
|
+
def path(*, **params)
|
|
31
|
+
return resolver_result(:path, *, **params) unless @helper
|
|
32
|
+
|
|
33
|
+
url_helpers.public_send(@helper, *, **params)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def url(*, **params)
|
|
37
|
+
return resolver_result(:url, *, **params) unless @helper
|
|
38
|
+
|
|
39
|
+
unless @helper.to_s.end_with?("_path")
|
|
40
|
+
raise Error, "#{name.inspect} declares helper #{@helper.inspect}, which does not end in `_path`"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
url_helpers.public_send(@helper.to_s.sub(/_path\z/, "_url"), *, **params)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def engine
|
|
47
|
+
@engine.is_a?(Proc) ? @engine.call : @engine
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def resolver_result(mode, *, **)
|
|
53
|
+
return @resolver.call(*, **) unless @engine
|
|
54
|
+
|
|
55
|
+
context = mode == :url ? UrlResolverContext.new(url_helpers) : url_helpers
|
|
56
|
+
context.instance_exec(*, **, &@resolver)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def url_helpers
|
|
60
|
+
engine.routes.url_helpers
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def resolve(value)
|
|
64
|
+
value.respond_to?(:call) ? value.call : value
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wayfinding
|
|
4
|
+
# The result of Wayfinding.of_kind. An Array of Destination with ability filtering.
|
|
5
|
+
class DestinationList < Array
|
|
6
|
+
# Destinations the given ability permits. Destinations without an action are
|
|
7
|
+
# excluded: there is nothing to authorize against.
|
|
8
|
+
def accessible_by(ability)
|
|
9
|
+
self.class.new(select do |destination|
|
|
10
|
+
destination.action && ability.can?(destination.action, destination.subject)
|
|
11
|
+
end)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wayfinding
|
|
4
|
+
Error = Class.new(StandardError)
|
|
5
|
+
|
|
6
|
+
# Raised when looking up a destination that was never registered.
|
|
7
|
+
UnregisteredDestination = Class.new(Error)
|
|
8
|
+
|
|
9
|
+
# Raised at registration when a destination is missing fields its kind requires,
|
|
10
|
+
# or when its resolution strategy is ambiguous or incomplete.
|
|
11
|
+
InvalidDestination = Class.new(Error)
|
|
12
|
+
|
|
13
|
+
# Raised when a destination declares a kind that was never defined.
|
|
14
|
+
UnknownKind = Class.new(Error)
|
|
15
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wayfinding
|
|
4
|
+
# A named field contract. Applications define kinds; Wayfinding only enforces them.
|
|
5
|
+
#
|
|
6
|
+
# Wayfinding.define_kind(:report, requires: %i[label description action subject])
|
|
7
|
+
#
|
|
8
|
+
# +requires+ may name any field, whether or not Wayfinding gives it behavior.
|
|
9
|
+
# Validation asserts presence only and never resolves callables, so a lazily
|
|
10
|
+
# registered <tt>subject: -> { ProjectTask }</tt> passes without autoloading.
|
|
11
|
+
class Kind
|
|
12
|
+
attr_reader :name, :requires
|
|
13
|
+
|
|
14
|
+
def initialize(name:, requires: [])
|
|
15
|
+
@name = name.to_sym
|
|
16
|
+
@requires = Array(requires).map(&:to_sym).freeze
|
|
17
|
+
freeze
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def validate!(destination_name, given)
|
|
21
|
+
missing = requires.reject { |field| given.key?(field) && !given[field].nil? }
|
|
22
|
+
return if missing.empty?
|
|
23
|
+
|
|
24
|
+
raise InvalidDestination,
|
|
25
|
+
"Destination #{destination_name.inspect} of kind #{name.inspect} " \
|
|
26
|
+
"is missing required #{missing.length == 1 ? 'field' : 'fields'}: #{missing.join(', ')}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "wayfinding"
|
|
4
|
+
|
|
5
|
+
module Wayfinding
|
|
6
|
+
# Spec helpers for applications consuming Wayfinding.
|
|
7
|
+
#
|
|
8
|
+
# require "wayfinding/rspec"
|
|
9
|
+
#
|
|
10
|
+
# Each example runs inside Wayfinding.preserve!, so destinations registered in
|
|
11
|
+
# a spec are rolled back afterwards. Use #stub_destination rather than mocking
|
|
12
|
+
# Wayfinding itself.
|
|
13
|
+
module RSpecHelpers
|
|
14
|
+
# Register a destination that resolves to a fixed value, with no engine or
|
|
15
|
+
# route helpers involved.
|
|
16
|
+
#
|
|
17
|
+
# stub_destination(:home, "/homes/1")
|
|
18
|
+
# stub_destination(:home) { |home| "/homes/#{home.id}" }
|
|
19
|
+
def stub_destination(name, path = nil, **attributes, &resolver)
|
|
20
|
+
resolver ||= proc { |*, **| path }
|
|
21
|
+
|
|
22
|
+
Wayfinding.register(name: name, **attributes, &resolver)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
RSpec.configure do |config|
|
|
28
|
+
config.include Wayfinding::RSpecHelpers
|
|
29
|
+
|
|
30
|
+
config.around do |example|
|
|
31
|
+
Wayfinding.preserve! { example.run }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wayfinding
|
|
4
|
+
# Evaluates a destination block as a URL by delegating route helper calls to
|
|
5
|
+
# the registered engine and replacing a trailing `_path` with `_url`.
|
|
6
|
+
class UrlResolverContext
|
|
7
|
+
def initialize(url_helpers)
|
|
8
|
+
@url_helpers = url_helpers
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def method_missing(name, ...)
|
|
14
|
+
@url_helpers.public_send(url_helper_name(name), ...)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def respond_to_missing?(name, include_private = false)
|
|
18
|
+
@url_helpers.respond_to?(url_helper_name(name), include_private)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def url_helper_name(name)
|
|
22
|
+
name.to_s.sub(/_path\z/, "_url")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/wayfinding.rb
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "wayfinding/errors"
|
|
4
|
+
require "wayfinding/kind"
|
|
5
|
+
require "wayfinding/url_resolver_context"
|
|
6
|
+
require "wayfinding/destination"
|
|
7
|
+
require "wayfinding/destination_list"
|
|
8
|
+
require "wayfinding/version"
|
|
9
|
+
|
|
10
|
+
# Cross-application URL resolution.
|
|
11
|
+
#
|
|
12
|
+
# Any component can link to any page without depending on the component that
|
|
13
|
+
# owns the route. Registered destinations are data, so this module's public
|
|
14
|
+
# surface never grows as destinations are added.
|
|
15
|
+
#
|
|
16
|
+
# Wayfinding.register(
|
|
17
|
+
# name: :home,
|
|
18
|
+
# engine: -> { Projects::Engine },
|
|
19
|
+
# helper: :home_path
|
|
20
|
+
# )
|
|
21
|
+
#
|
|
22
|
+
# Wayfinding.path_for(:home, home, current_tab: "Projects")
|
|
23
|
+
module Wayfinding
|
|
24
|
+
class << self
|
|
25
|
+
# Declare a named field contract. See Wayfinding::Kind.
|
|
26
|
+
def define_kind(name, requires: [])
|
|
27
|
+
kind = Kind.new(name: name, requires: requires)
|
|
28
|
+
kinds[kind.name] = kind
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def kinds = @kinds ||= {}
|
|
32
|
+
|
|
33
|
+
def register(name:, engine: nil, kind: nil, helper: nil, **attributes, &resolver)
|
|
34
|
+
validate_resolution!(name, engine, helper, resolver)
|
|
35
|
+
validate_all_or_none!(name, attributes, %i[action subject])
|
|
36
|
+
validate_kind!(name, kind, **attributes)
|
|
37
|
+
|
|
38
|
+
destinations[name.to_sym] = Destination.new(
|
|
39
|
+
name: name, engine: engine, kind: kind, helper: helper, resolver: resolver, **attributes
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def path_for(name, *, **params) = fetch(name).path(*, **params)
|
|
44
|
+
|
|
45
|
+
def url_for(name, *, **params) = fetch(name).url(*, **params)
|
|
46
|
+
|
|
47
|
+
def of_kind(name)
|
|
48
|
+
name = name.to_sym
|
|
49
|
+
DestinationList.new(destinations.each_value.select { |destination| destination.kind == name })
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def fetch(name)
|
|
53
|
+
destinations.fetch(name.to_sym) do
|
|
54
|
+
raise UnregisteredDestination,
|
|
55
|
+
"No destination registered for #{name.inspect}. Registered: #{registered_names.join(', ')}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def registered?(name) = destinations.key?(name.to_sym)
|
|
60
|
+
|
|
61
|
+
def registered_names = destinations.keys.sort
|
|
62
|
+
|
|
63
|
+
# Assert every expected destination is registered. Call at the end of an
|
|
64
|
+
# initializer so a dropped registration fails the boot rather than rendering
|
|
65
|
+
# a broken link.
|
|
66
|
+
def verify!(expected)
|
|
67
|
+
missing = Array(expected).map(&:to_sym) - registered_names
|
|
68
|
+
return true if missing.empty?
|
|
69
|
+
|
|
70
|
+
raise UnregisteredDestination, "Missing destination registrations: #{missing.join(', ')}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Snapshot the registry, yield, then restore it. Used by the RSpec helper so
|
|
74
|
+
# a destination registered inside an example does not leak.
|
|
75
|
+
def preserve!
|
|
76
|
+
destinations_snapshot = destinations.dup
|
|
77
|
+
kinds_snapshot = kinds.dup
|
|
78
|
+
yield
|
|
79
|
+
ensure
|
|
80
|
+
@destinations = destinations_snapshot
|
|
81
|
+
@kinds = kinds_snapshot
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def reset!
|
|
85
|
+
@destinations = {}
|
|
86
|
+
@kinds = {}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def destinations = @destinations ||= {}
|
|
92
|
+
|
|
93
|
+
def validate_resolution!(name, engine, helper, resolver)
|
|
94
|
+
raise InvalidDestination, "#{name.inspect} declares both `helper:` and a block; use one" if helper && resolver
|
|
95
|
+
raise InvalidDestination, "#{name.inspect} declares neither `helper:` nor a block" unless helper || resolver
|
|
96
|
+
raise InvalidDestination, "#{name.inspect} declares `helper:` without an `engine:`" if helper && engine.nil?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def validate_all_or_none!(name, attributes, fields)
|
|
100
|
+
supplied = fields.reject { |field| attributes[field].nil? }
|
|
101
|
+
return if supplied.empty? || supplied.length == fields.length
|
|
102
|
+
|
|
103
|
+
missing = fields - supplied
|
|
104
|
+
raise InvalidDestination,
|
|
105
|
+
"#{name.inspect} must declare #{fields.map { |field| "`#{field}:`" }.join(' and ')} together; " \
|
|
106
|
+
"missing #{missing.map { |field| "`#{field}:`" }.join(', ')}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def validate_kind!(name, kind, **given)
|
|
110
|
+
return if kind.nil?
|
|
111
|
+
|
|
112
|
+
definition = kinds.fetch(kind.to_sym) do
|
|
113
|
+
raise UnknownKind, "#{name.inspect} declares unknown kind #{kind.inspect}. Defined: #{kinds.keys.join(', ')}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
definition.validate!(name, given)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: wayfinding
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nitro Developers
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: appraisal
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 2.5.0
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 2.5.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: bundler
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.1'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.1'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: license_finder
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '7.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '7.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: pry
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.14'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.14'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: pry-byebug
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - '='
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 3.10.1
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - '='
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 3.10.1
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: rainbow
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - '='
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 3.1.1
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - '='
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 3.1.1
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: rake
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '13.0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '13.0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: rspec
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '3.0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '3.0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: simplecov
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - '='
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: 0.15.1
|
|
131
|
+
type: :development
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - '='
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: 0.15.1
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: yard
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - '='
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: 0.9.38
|
|
145
|
+
type: :development
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - '='
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: 0.9.38
|
|
152
|
+
description: A global registry of named destinations, so any component can link to
|
|
153
|
+
any page without depending on the component that owns the route.
|
|
154
|
+
email:
|
|
155
|
+
- dev@powerhrg.com
|
|
156
|
+
executables: []
|
|
157
|
+
extensions: []
|
|
158
|
+
extra_rdoc_files: []
|
|
159
|
+
files:
|
|
160
|
+
- Rakefile
|
|
161
|
+
- docs/README.md
|
|
162
|
+
- lib/wayfinding.rb
|
|
163
|
+
- lib/wayfinding/destination.rb
|
|
164
|
+
- lib/wayfinding/destination_list.rb
|
|
165
|
+
- lib/wayfinding/errors.rb
|
|
166
|
+
- lib/wayfinding/kind.rb
|
|
167
|
+
- lib/wayfinding/rspec.rb
|
|
168
|
+
- lib/wayfinding/url_resolver_context.rb
|
|
169
|
+
- lib/wayfinding/version.rb
|
|
170
|
+
homepage: https://github.com/powerhome/power-tools
|
|
171
|
+
licenses:
|
|
172
|
+
- MIT
|
|
173
|
+
metadata:
|
|
174
|
+
rubygems_mfa_required: 'true'
|
|
175
|
+
rdoc_options: []
|
|
176
|
+
require_paths:
|
|
177
|
+
- lib
|
|
178
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
179
|
+
requirements:
|
|
180
|
+
- - ">="
|
|
181
|
+
- !ruby/object:Gem::Version
|
|
182
|
+
version: '3.2'
|
|
183
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - ">="
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '0'
|
|
188
|
+
requirements: []
|
|
189
|
+
rubygems_version: 4.0.3
|
|
190
|
+
specification_version: 4
|
|
191
|
+
summary: Cross-application URL resolution.
|
|
192
|
+
test_files: []
|