toolchest 0.3.2 → 0.3.5
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 +4 -4
- data/LLMS.txt +22 -1
- data/README.md +83 -1
- data/app/controllers/toolchest/application_controller.rb +5 -0
- data/app/controllers/toolchest/oauth/authorizations_controller.rb +1 -1
- data/app/controllers/toolchest/oauth/authorized_applications_controller.rb +1 -1
- data/app/controllers/toolchest/oauth/metadata_controller.rb +1 -1
- data/app/controllers/toolchest/oauth/registrations_controller.rb +9 -3
- data/app/controllers/toolchest/oauth/tokens_controller.rb +4 -3
- data/app/models/toolchest/oauth_access_grant.rb +7 -0
- data/lib/generators/toolchest/consent_generator.rb +3 -2
- data/lib/generators/toolchest/oauth_views_generator.rb +10 -8
- data/lib/toolchest/renderer.rb +12 -0
- data/lib/toolchest/route_delegation.rb +27 -0
- data/lib/toolchest/router.rb +10 -1
- data/lib/toolchest/tool_definition.rb +3 -2
- data/lib/toolchest/toolbox.rb +42 -2
- data/lib/toolchest/version.rb +1 -1
- data/lib/toolchest.rb +18 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db7d29ec671adac8f24512bf3d190dedb55250dfd846b31c5906feac16771130
|
|
4
|
+
data.tar.gz: a3c7414f1c60e5997942fdacfa70ee3a368e6a0cd2b237eab2b1d187351cb10d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 68779c02c513ba5dc8052407a015de6fddda8e44c5aa6add4daeb204eb08fe7b41b997a6a2791e90b62ab1dc558d467d3ad722cbee5baca32fccbe7a04692203
|
|
7
|
+
data.tar.gz: d63e96b09cdf89b09a31e902b593f55b4399ab347c5af3daa033c50c03054c654e0338fe2f9eb31544f19522e65bb1c30f9181c33a040e14a2c26dbf91b5371c
|
data/LLMS.txt
CHANGED
|
@@ -213,7 +213,12 @@ end
|
|
|
213
213
|
|
|
214
214
|
Types: `:string`, `:integer`, `:number`, `:boolean`, `:object`, `[:object]`, `[:string]`, etc.
|
|
215
215
|
|
|
216
|
-
Options on `tool`:
|
|
216
|
+
Options on `tool`:
|
|
217
|
+
|
|
218
|
+
- `name: "custom_tool_name"` — override generated tool name
|
|
219
|
+
- `access: :read` or `access: :write` — scope filtering + MCP annotations
|
|
220
|
+
- `scope: "admin"` or `scope: ["admin", "superuser"]` — per-tool scope override (see below)
|
|
221
|
+
- `annotations: { openWorldHint: true }` — override MCP hints
|
|
217
222
|
|
|
218
223
|
`access: :read` → `readOnlyHint: true, destructiveHint: false`. `access: :write` → `readOnlyHint: false, destructiveHint: true`.
|
|
219
224
|
|
|
@@ -345,6 +350,22 @@ Scopes filter `tools/list` — clients only see tools their token allows. Fails
|
|
|
345
350
|
|
|
346
351
|
Disable: `config.filter_tools_by_scope = false`
|
|
347
352
|
|
|
353
|
+
### Per-tool scope override
|
|
354
|
+
|
|
355
|
+
When a tool doesn't fit its toolbox's scope boundary, set `scope:` to bypass the convention:
|
|
356
|
+
|
|
357
|
+
```ruby
|
|
358
|
+
tool "Move ticket", scope: "admin" do # only "admin" scope grants access
|
|
359
|
+
param :status, :string, "New status"
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
tool "Escalate", scope: ["admin", "superuser"] do # either scope works (OR)
|
|
363
|
+
param :id, :string, "ID"
|
|
364
|
+
end
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
`scope:` replaces convention matching for that tool. `tickets:write` won't grant access to a tool with `scope: "admin"`. Enforced on both `tools/list` and `tools/call`.
|
|
368
|
+
|
|
348
369
|
### Optional scopes (checkboxes on consent)
|
|
349
370
|
|
|
350
371
|
```ruby
|
data/README.md
CHANGED
|
@@ -31,6 +31,8 @@ rails s
|
|
|
31
31
|
```ruby
|
|
32
32
|
# app/toolboxes/application_toolbox.rb
|
|
33
33
|
class ApplicationToolbox < Toolchest::Toolbox
|
|
34
|
+
helper_method :current_user
|
|
35
|
+
|
|
34
36
|
def current_user = auth&.resource_owner
|
|
35
37
|
|
|
36
38
|
rescue_from ActiveRecord::RecordNotFound do |e|
|
|
@@ -39,7 +41,7 @@ class ApplicationToolbox < Toolchest::Toolbox
|
|
|
39
41
|
end
|
|
40
42
|
```
|
|
41
43
|
|
|
42
|
-
This is your ApplicationController. `auth` returns a `Toolchest::AuthContext` with `.resource_owner` (whatever your `authenticate` block returns), `.scopes` (always from the token), and `.token` (the raw record). Define `current_user` as a convenience
|
|
44
|
+
This is your ApplicationController. `auth` returns a `Toolchest::AuthContext` with `.resource_owner` (whatever your `authenticate` block returns), `.scopes` (always from the token), and `.token` (the raw record). Define `current_user` as a convenience and expose it to views with `helper_method`, add shared error handling, include your gems.
|
|
43
45
|
|
|
44
46
|
```ruby
|
|
45
47
|
# app/toolboxes/orders_toolbox.rb
|
|
@@ -130,6 +132,41 @@ render_errors @order # MCP error from ActiveModel errors
|
|
|
130
132
|
|
|
131
133
|
`render :show` after mutations. Most toolboxes need one or two views.
|
|
132
134
|
|
|
135
|
+
### View helpers
|
|
136
|
+
|
|
137
|
+
Toolbox methods aren't available in views by default — views only get instance variables. Use `helper_method` to expose methods, same as a Rails controller:
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
class ApplicationToolbox < Toolchest::Toolbox
|
|
141
|
+
helper_method :current_user, :admin?
|
|
142
|
+
|
|
143
|
+
def current_user = auth&.resource_owner
|
|
144
|
+
def admin? = current_user&.admin?
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Now `current_user` and `admin?` work in any view:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
# app/views/toolboxes/orders/show.json.jb
|
|
152
|
+
{
|
|
153
|
+
id: @order.id,
|
|
154
|
+
status: @order.status,
|
|
155
|
+
viewer: current_user.name,
|
|
156
|
+
can_edit: admin?
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Include entire modules with `helper`:
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
class ApplicationToolbox < Toolchest::Toolbox
|
|
164
|
+
helper FormattingHelper
|
|
165
|
+
end
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Both `helper_method` and `helper` inherit through the toolbox hierarchy — define them in `ApplicationToolbox` and every toolbox gets them.
|
|
169
|
+
|
|
133
170
|
### Resources and prompts
|
|
134
171
|
|
|
135
172
|
Supported, though most toolboxes won't need them.
|
|
@@ -360,6 +397,25 @@ Toolchest::OauthAccessGrant.revoke_all_for(app, user.id)
|
|
|
360
397
|
app.destroy # cascades to all grants and tokens
|
|
361
398
|
```
|
|
362
399
|
|
|
400
|
+
### Controller behavior
|
|
401
|
+
|
|
402
|
+
The consent screen and authorized applications page inherit from your app's `ApplicationController` by default — so they get your auth, layout, nav, etc. Route helpers like `root_path` in your layout work automatically (Toolchest delegates unresolved `_path`/`_url` helpers to `main_app`).
|
|
403
|
+
|
|
404
|
+
If you don't want host app behavior on these pages:
|
|
405
|
+
|
|
406
|
+
```ruby
|
|
407
|
+
# config/initializers/toolchest.rb
|
|
408
|
+
Toolchest.base_controller = "ActionController::Base"
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
To disable the route helper delegation:
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
Toolchest.delegate_route_helpers = false
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
API-only endpoints (token exchange, metadata, dynamic registration) always use `ActionController::API` regardless of this setting.
|
|
418
|
+
|
|
363
419
|
### Custom
|
|
364
420
|
|
|
365
421
|
If the built-in strategies don't fit, pass any object that responds to `#authenticate(request)`:
|
|
@@ -421,6 +477,32 @@ end
|
|
|
421
477
|
|
|
422
478
|
Turn it off: `config.filter_tools_by_scope = false`
|
|
423
479
|
|
|
480
|
+
### Per-tool scope override
|
|
481
|
+
|
|
482
|
+
The convention derives scopes from the toolbox name — `OrdersToolbox` tools require `orders:*`. When a tool doesn't fit its toolbox's scope boundary, override it:
|
|
483
|
+
|
|
484
|
+
```ruby
|
|
485
|
+
class TicketsToolbox < ApplicationToolbox
|
|
486
|
+
tool "List tickets" do
|
|
487
|
+
end
|
|
488
|
+
def list = # ... requires tickets:read (convention)
|
|
489
|
+
|
|
490
|
+
# Only tokens with the "admin" scope can see or call this tool
|
|
491
|
+
tool "Move ticket", scope: "admin" do
|
|
492
|
+
param :status, :string, "New status"
|
|
493
|
+
end
|
|
494
|
+
def move = # ...
|
|
495
|
+
|
|
496
|
+
# Either "admin" OR "superuser" grants access
|
|
497
|
+
tool "Escalate ticket", scope: ["admin", "superuser"] do
|
|
498
|
+
param :id, :string, "Ticket ID"
|
|
499
|
+
end
|
|
500
|
+
def escalate = # ...
|
|
501
|
+
end
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
`scope:` replaces the convention entirely for that tool — `tickets:write` won't grant access to a tool with `scope: "admin"`. The token must include the literal scope string. Enforced on both `tools/list` (visibility) and `tools/call` (execution).
|
|
505
|
+
|
|
424
506
|
### Optional scopes (checkboxes)
|
|
425
507
|
|
|
426
508
|
By default, the consent screen is all-or-nothing — approve all requested scopes or deny. Enable `optional_scopes` and users get checkboxes:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
module Toolchest
|
|
2
2
|
module Oauth
|
|
3
|
-
class RegistrationsController < ::
|
|
4
|
-
skip_forgery_protection
|
|
3
|
+
class RegistrationsController < ActionController::API
|
|
5
4
|
wrap_parameters false
|
|
6
5
|
|
|
7
6
|
# POST /register — Dynamic Client Registration (RFC 7591)
|
|
@@ -9,7 +8,14 @@ module Toolchest
|
|
|
9
8
|
# at authorization time via the resource param.
|
|
10
9
|
def create
|
|
11
10
|
name = (params[:client_name] || "MCP Client").truncate(255)
|
|
12
|
-
uris = Array(params[:redirect_uris])
|
|
11
|
+
uris = Array(params[:redirect_uris]).map(&:to_s)
|
|
12
|
+
|
|
13
|
+
if uris.any? { |u| u.match?(/[\r\n]/) }
|
|
14
|
+
return render json: {
|
|
15
|
+
error: "invalid_client_metadata",
|
|
16
|
+
error_description: "Redirect URIs must not contain newlines"
|
|
17
|
+
}, status: :bad_request
|
|
18
|
+
end
|
|
13
19
|
|
|
14
20
|
if uris.size > 10
|
|
15
21
|
return render json: {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
module Toolchest
|
|
2
2
|
module Oauth
|
|
3
|
-
class TokensController < ::
|
|
4
|
-
skip_forgery_protection
|
|
3
|
+
class TokensController < ActionController::API
|
|
5
4
|
|
|
6
5
|
# POST /mcp/oauth/token
|
|
7
6
|
def create
|
|
@@ -46,7 +45,9 @@ module Toolchest
|
|
|
46
45
|
return error_response("invalid_grant", "PKCE verification failed")
|
|
47
46
|
end
|
|
48
47
|
|
|
49
|
-
grant.
|
|
48
|
+
unless grant.revoke_atomically!
|
|
49
|
+
return error_response("invalid_grant", "Authorization code already used")
|
|
50
|
+
end
|
|
50
51
|
|
|
51
52
|
token = Toolchest::OauthAccessToken.create_for(
|
|
52
53
|
application: app,
|
|
@@ -24,6 +24,13 @@ module Toolchest
|
|
|
24
24
|
|
|
25
25
|
def revoke! = update!(revoked_at: Time.current)
|
|
26
26
|
|
|
27
|
+
# Atomic revocation — returns true only if THIS call revoked the grant.
|
|
28
|
+
# Prevents race conditions where two concurrent token exchanges both
|
|
29
|
+
# find the grant active and both mint tokens (RFC 6749 §4.1.2).
|
|
30
|
+
def revoke_atomically!
|
|
31
|
+
self.class.where(id: id, revoked_at: nil).update_all(revoked_at: Time.current) > 0
|
|
32
|
+
end
|
|
33
|
+
|
|
27
34
|
def uses_pkce? = code_challenge.present?
|
|
28
35
|
|
|
29
36
|
def verify_pkce(code_verifier)
|
|
@@ -4,11 +4,12 @@ require "rails/generators/base"
|
|
|
4
4
|
module Toolchest
|
|
5
5
|
module Generators
|
|
6
6
|
class ConsentGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("../../../..", __dir__)
|
|
8
|
+
|
|
7
9
|
desc "Eject the OAuth consent view for customization"
|
|
8
10
|
|
|
9
11
|
def copy_consent_view
|
|
10
|
-
|
|
11
|
-
copy_file engine_view, "app/views/toolchest/oauth/authorizations/new.html.erb"
|
|
12
|
+
copy_file "app/views/toolchest/oauth/authorizations/new.html.erb"
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def show_instructions
|
|
@@ -4,23 +4,25 @@ require "rails/generators/base"
|
|
|
4
4
|
module Toolchest
|
|
5
5
|
module Generators
|
|
6
6
|
class OauthViewsGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("../../../..", __dir__)
|
|
8
|
+
|
|
7
9
|
desc "Eject all OAuth views and controllers for customization"
|
|
8
10
|
|
|
9
11
|
def copy_views
|
|
10
|
-
|
|
12
|
+
views_dir = File.join(self.class.source_root, "app/views/toolchest/oauth")
|
|
11
13
|
|
|
12
|
-
Dir[File.join(
|
|
13
|
-
relative = src.sub(
|
|
14
|
-
copy_file
|
|
14
|
+
Dir[File.join(views_dir, "**", "*.erb")].each do |src|
|
|
15
|
+
relative = src.sub(views_dir + "/", "")
|
|
16
|
+
copy_file "app/views/toolchest/oauth/#{relative}"
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def copy_controllers
|
|
19
|
-
|
|
21
|
+
controllers_dir = File.join(self.class.source_root, "app/controllers/toolchest/oauth")
|
|
20
22
|
|
|
21
|
-
Dir[File.join(
|
|
22
|
-
relative = src.sub(
|
|
23
|
-
copy_file
|
|
23
|
+
Dir[File.join(controllers_dir, "**", "*.rb")].each do |src|
|
|
24
|
+
relative = src.sub(controllers_dir + "/", "")
|
|
25
|
+
copy_file "app/controllers/toolchest/oauth/#{relative}"
|
|
24
26
|
end
|
|
25
27
|
end
|
|
26
28
|
|
data/lib/toolchest/renderer.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Toolchest
|
|
|
12
12
|
|
|
13
13
|
lookup = ActionView::LookupContext.new(view_paths)
|
|
14
14
|
view = ActionView::Base.with_empty_template_cache.new(lookup, assigns, nil)
|
|
15
|
+
apply_helpers(view, toolbox)
|
|
15
16
|
|
|
16
17
|
result = view.render(template: template_name, formats: [:json])
|
|
17
18
|
|
|
@@ -73,6 +74,17 @@ module Toolchest
|
|
|
73
74
|
assigns
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
def apply_helpers(view, toolbox)
|
|
78
|
+
toolbox.class.helper_modules.each { |mod| view.extend(mod) }
|
|
79
|
+
|
|
80
|
+
toolbox.class.helper_methods.each do |method_name|
|
|
81
|
+
tb = toolbox
|
|
82
|
+
view.define_singleton_method(method_name) do |*args, **kwargs, &block|
|
|
83
|
+
tb.send(method_name, *args, **kwargs, &block)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
76
88
|
def view_paths
|
|
77
89
|
paths = []
|
|
78
90
|
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Toolchest
|
|
2
|
+
# Delegates unresolved _path/_url helpers to main_app so the host
|
|
3
|
+
# application's layout works inside engine-rendered views without
|
|
4
|
+
# requiring main_app. prefixes everywhere.
|
|
5
|
+
#
|
|
6
|
+
# Included as a view helper by the engine when
|
|
7
|
+
# Toolchest.delegate_route_helpers is true (the default).
|
|
8
|
+
#
|
|
9
|
+
# To disable:
|
|
10
|
+
#
|
|
11
|
+
# # config/initializers/toolchest.rb
|
|
12
|
+
# Toolchest.delegate_route_helpers = false
|
|
13
|
+
#
|
|
14
|
+
module RouteDelegation
|
|
15
|
+
def method_missing(method, *args, **kwargs, &block)
|
|
16
|
+
if method.to_s.end_with?("_path", "_url") && main_app.respond_to?(method)
|
|
17
|
+
main_app.public_send(method, *args, **kwargs, &block)
|
|
18
|
+
else
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def respond_to_missing?(method, include_private = false)
|
|
24
|
+
(method.to_s.end_with?("_path", "_url") && main_app.respond_to?(method)) || super
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/toolchest/router.rb
CHANGED
|
@@ -217,7 +217,16 @@ module Toolchest
|
|
|
217
217
|
READ_ACTIONS = Set.new(%i[show index list search]).freeze
|
|
218
218
|
|
|
219
219
|
def tool_allowed_by_scopes?(tool_definition, scopes)
|
|
220
|
-
|
|
220
|
+
if scopes.empty?
|
|
221
|
+
# Token has no scopes. Allow only if the server hasn't declared any
|
|
222
|
+
# scopes either (convention-based filtering without declarations).
|
|
223
|
+
# When scopes ARE configured, empty token scopes = no access (fail closed).
|
|
224
|
+
return Toolchest.configuration(@mount_key).scopes.empty?
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
if tool_definition.scope
|
|
228
|
+
return tool_definition.scope.any? { |s| scopes.include?(s) }
|
|
229
|
+
end
|
|
221
230
|
|
|
222
231
|
prefix = tool_definition.toolbox_class.controller_name.split("/").last
|
|
223
232
|
tool_access = tool_definition.access_level ||
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
module Toolchest
|
|
2
2
|
class ToolDefinition
|
|
3
|
-
attr_reader :method_name, :description, :params, :toolbox_class, :custom_name, :access_level, :annotations
|
|
3
|
+
attr_reader :method_name, :description, :params, :toolbox_class, :custom_name, :access_level, :scope, :annotations
|
|
4
4
|
|
|
5
|
-
def initialize(method_name:, description:, params:, toolbox_class:, custom_name: nil, access_level: nil, annotations: nil)
|
|
5
|
+
def initialize(method_name:, description:, params:, toolbox_class:, custom_name: nil, access_level: nil, scope: nil, annotations: nil)
|
|
6
6
|
@method_name = method_name.to_sym
|
|
7
7
|
@description = description
|
|
8
8
|
@params = params
|
|
9
9
|
@toolbox_class = toolbox_class
|
|
10
10
|
@custom_name = custom_name
|
|
11
11
|
@access_level = access_level
|
|
12
|
+
@scope = scope ? Array(scope) : nil
|
|
12
13
|
@annotations = annotations
|
|
13
14
|
end
|
|
14
15
|
|
data/lib/toolchest/toolbox.rb
CHANGED
|
@@ -16,6 +16,8 @@ module Toolchest
|
|
|
16
16
|
subclass.instance_variable_set(:@_resources, [])
|
|
17
17
|
subclass.instance_variable_set(:@_prompts, [])
|
|
18
18
|
subclass.instance_variable_set(:@_pending_tool, nil)
|
|
19
|
+
subclass.instance_variable_set(:@_helper_methods, [])
|
|
20
|
+
subclass.instance_variable_set(:@_helper_modules, [])
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
def tool_definitions
|
|
@@ -46,10 +48,10 @@ module Toolchest
|
|
|
46
48
|
.flat_map { |a| a.send(:own_prompts) }
|
|
47
49
|
end
|
|
48
50
|
|
|
49
|
-
def tool(description, name: nil, access: nil, annotations: nil, &block)
|
|
51
|
+
def tool(description, name: nil, access: nil, scope: nil, annotations: nil, &block)
|
|
50
52
|
builder = ToolBuilder.new
|
|
51
53
|
builder.instance_eval(&block) if block
|
|
52
|
-
@_pending_tool = { description
|
|
54
|
+
@_pending_tool = { description:, custom_name: name, access_level: access, scope:, annotations:, builder: }
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
def default_param(name, type, description = "", **options)
|
|
@@ -110,12 +112,46 @@ module Toolchest
|
|
|
110
112
|
toolbox_class: self,
|
|
111
113
|
custom_name: pending[:custom_name],
|
|
112
114
|
access_level: pending[:access_level],
|
|
115
|
+
scope: pending[:scope],
|
|
113
116
|
annotations: pending[:annotations]
|
|
114
117
|
)
|
|
115
118
|
|
|
116
119
|
@_tool_definitions[method_name.to_sym] = definition
|
|
117
120
|
end
|
|
118
121
|
|
|
122
|
+
# Expose toolbox methods as view helpers, like controller helper_method.
|
|
123
|
+
#
|
|
124
|
+
# helper_method :current_user, :admin?
|
|
125
|
+
#
|
|
126
|
+
def helper_method(*methods)
|
|
127
|
+
@_helper_methods.concat(methods.map(&:to_sym))
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Include modules as view helpers.
|
|
131
|
+
#
|
|
132
|
+
# helper ApplicationHelper
|
|
133
|
+
# helper FormattingHelper, CurrencyHelper
|
|
134
|
+
#
|
|
135
|
+
def helper(*modules)
|
|
136
|
+
@_helper_modules.concat(modules)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def helper_methods
|
|
140
|
+
ancestors
|
|
141
|
+
.select { |a| a.respond_to?(:own_helper_methods, true) }
|
|
142
|
+
.reverse
|
|
143
|
+
.flat_map { |a| a.send(:own_helper_methods) }
|
|
144
|
+
.uniq
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def helper_modules
|
|
148
|
+
ancestors
|
|
149
|
+
.select { |a| a.respond_to?(:own_helper_modules, true) }
|
|
150
|
+
.reverse
|
|
151
|
+
.flat_map { |a| a.send(:own_helper_modules) }
|
|
152
|
+
.uniq
|
|
153
|
+
end
|
|
154
|
+
|
|
119
155
|
def controller_name = name&.underscore&.chomp("_toolbox") || "anonymous"
|
|
120
156
|
|
|
121
157
|
protected
|
|
@@ -127,6 +163,10 @@ module Toolchest
|
|
|
127
163
|
def own_resources = @_resources || []
|
|
128
164
|
|
|
129
165
|
def own_prompts = @_prompts || []
|
|
166
|
+
|
|
167
|
+
def own_helper_methods = @_helper_methods || []
|
|
168
|
+
|
|
169
|
+
def own_helper_modules = @_helper_modules || []
|
|
130
170
|
end
|
|
131
171
|
|
|
132
172
|
attr_reader :params
|
data/lib/toolchest/version.rb
CHANGED
data/lib/toolchest.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "toolchest/version"
|
|
|
4
4
|
|
|
5
5
|
module Toolchest
|
|
6
6
|
autoload :App, "toolchest/app"
|
|
7
|
+
autoload :RouteDelegation, "toolchest/route_delegation"
|
|
7
8
|
autoload :AuthContext, "toolchest/auth_context"
|
|
8
9
|
autoload :Configuration, "toolchest/configuration"
|
|
9
10
|
autoload :Current, "toolchest/current"
|
|
@@ -68,11 +69,28 @@ module Toolchest
|
|
|
68
69
|
# When multiple OAuth mounts exist, bare /.well-known/* resolves to this mount
|
|
69
70
|
attr_accessor :default_oauth_mount
|
|
70
71
|
|
|
72
|
+
# Parent class for engine HTML controllers. Default: "::ApplicationController".
|
|
73
|
+
# Set to "ActionController::Base" to avoid inheriting host app behavior.
|
|
74
|
+
attr_writer :base_controller
|
|
75
|
+
def base_controller
|
|
76
|
+
@base_controller || "::ApplicationController"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Delegate unresolved _path/_url helpers to main_app so the host
|
|
80
|
+
# layout renders correctly inside engine views. Default: true.
|
|
81
|
+
# Set to false in an initializer to disable.
|
|
82
|
+
attr_writer :delegate_route_helpers
|
|
83
|
+
def delegate_route_helpers
|
|
84
|
+
@delegate_route_helpers != false
|
|
85
|
+
end
|
|
86
|
+
|
|
71
87
|
def reset!
|
|
72
88
|
@configs = nil
|
|
73
89
|
@routers = nil
|
|
74
90
|
@apps = nil
|
|
75
91
|
@default_oauth_mount = nil
|
|
92
|
+
@base_controller = nil
|
|
93
|
+
@delegate_route_helpers = nil
|
|
76
94
|
end
|
|
77
95
|
|
|
78
96
|
# Reset only routers/apps (preserves config set by initializers)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: toolchest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nora
|
|
@@ -46,6 +46,7 @@ files:
|
|
|
46
46
|
- LICENSE
|
|
47
47
|
- LLMS.txt
|
|
48
48
|
- README.md
|
|
49
|
+
- app/controllers/toolchest/application_controller.rb
|
|
49
50
|
- app/controllers/toolchest/oauth/authorizations_controller.rb
|
|
50
51
|
- app/controllers/toolchest/oauth/authorized_applications_controller.rb
|
|
51
52
|
- app/controllers/toolchest/oauth/metadata_controller.rb
|
|
@@ -88,6 +89,7 @@ files:
|
|
|
88
89
|
- lib/toolchest/parameters.rb
|
|
89
90
|
- lib/toolchest/rack_app.rb
|
|
90
91
|
- lib/toolchest/renderer.rb
|
|
92
|
+
- lib/toolchest/route_delegation.rb
|
|
91
93
|
- lib/toolchest/router.rb
|
|
92
94
|
- lib/toolchest/rspec.rb
|
|
93
95
|
- lib/toolchest/sampling_builder.rb
|