deacon-rails 1.0.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +99 -0
- data/lib/deacon/rails/helper.rb +82 -0
- data/lib/deacon/rails/railtie.rb +16 -0
- data/lib/deacon/rails/version.rb +5 -0
- data/lib/deacon/rails.rb +3 -0
- metadata +82 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b9b67729a41edf00bed6786b7b452d46280d312c38add74d0a8dba996c9110b2
|
|
4
|
+
data.tar.gz: 2424fcf30dc687f72f2438a837db3b90560eda33c6f8a89a22ace6c756a4e682
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d58bd949062b18345c26f8a706486d5deea5e9d5e956b36861113f9c8525fc5e9326bbb4199affd8a4b8ba0b4b61b03e87184648ae5db5f78b45fb799d0f1ed4
|
|
7
|
+
data.tar.gz: 992cd18343847745069d576eefc7443854c6d8117edfc568fd1a5a3fa08fa3632200d6626fd313c47b8984e35219bf0ea1c2a795e8e3defe79431e72e38d1b6e
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Deacon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# deacon-rails
|
|
2
|
+
|
|
3
|
+
[Deacon](https://heydeacon.com) is an AI support agent that answers questions from your own docs and content, and says when it doesn't know. This gem adds its chat widget to every page of a Rails app.
|
|
4
|
+
|
|
5
|
+
You need a Deacon account, and there's a free plan.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
bundle add deacon-rails
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Set it up
|
|
14
|
+
|
|
15
|
+
Put the helper in the `<head>` of `app/views/layouts/application.html.erb`:
|
|
16
|
+
|
|
17
|
+
```erb
|
|
18
|
+
<head>
|
|
19
|
+
<%# ...the rest of your head %>
|
|
20
|
+
<%= javascript_importmap_tags %>
|
|
21
|
+
<%= deacon_widget_tag %>
|
|
22
|
+
</head>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then give it your key, in the `DEACON_KEY` environment variable:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
DEACON_KEY=pk_your_widget_key
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or in an initializer, such as `config/initializers/deacon.rb`, which wins over `DEACON_KEY`:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
Rails.application.config.deacon.key = "pk_your_widget_key"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
To find your key, open Install from the button at the top of the Deacon dashboard. The snippet there has it in `data-key`, and it starts with `pk_`. It isn't a secret: every visitor's browser reads it. If the key is missing, the helper prints nothing and logs a warning that says why, so your pages still render. An option of the wrong type raises an `ArgumentError` that says why.
|
|
38
|
+
|
|
39
|
+
Deacon only runs on the sites listed on Install. A listed domain covers its subdomains, so `example.com` covers `app.example.com`. If your app lives somewhere else, such as `example.fly.dev`, add that address on Install too.
|
|
40
|
+
|
|
41
|
+
When you run `bin/rails server`, Install notices the widget on `localhost:3000` and lets you add it in one click. Chats on localhost don't count towards your plan.
|
|
42
|
+
|
|
43
|
+
If you tell Deacon who is signed in, with the `window.deaconUser` script from [Deacon's install guide](https://heydeacon.com/agent/install.md), put that script above `<%= deacon_widget_tag %>` in the head, because the widget reads it as it starts.
|
|
44
|
+
|
|
45
|
+
## Options
|
|
46
|
+
|
|
47
|
+
Set them on `config.deacon`, in an initializer or in `config/application.rb`.
|
|
48
|
+
|
|
49
|
+
| Option | Type | Default | What it does |
|
|
50
|
+
| ------------ | ------- | ------------ | ------------ |
|
|
51
|
+
| `key` | string | `DEACON_KEY` | Your Deacon key. |
|
|
52
|
+
| `context` | hash | none | Details Deacon takes into account when it answers, the same on every page, such as `{ product: "api", docs_version: "3.2" }`. Up to 12 keys, each value a string, number or boolean. Anyone can read them in the page's HTML, so never put a secret here. |
|
|
53
|
+
| `page_views` | boolean | `true` | The same script counts visits for Deacon's website analytics. Set this to `false` to send no page views or JavaScript errors from your app. Chat still works. |
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
Rails.application.config.deacon.key = "pk_your_widget_key"
|
|
57
|
+
Rails.application.config.deacon.context = { product: "api", docs_version: "3.2" }
|
|
58
|
+
Rails.application.config.deacon.page_views = false
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Turbo
|
|
62
|
+
|
|
63
|
+
Turbo Drive swaps the `<body>` on each visit and keeps the `<head>`, so in the head the tag runs once. Deacon puts its chat on `<html>`, outside the body, so the widget stays in place as people move between pages.
|
|
64
|
+
|
|
65
|
+
## Content Security Policy
|
|
66
|
+
|
|
67
|
+
If your app gives each request a nonce, with `config.content_security_policy_nonce_generator`, the tag carries it. The policy also needs Deacon's address, `https://heydeacon.com`, in `script-src`, `connect-src` and `frame-src`, and `'unsafe-inline'` in `style-src`, because the widget writes its styles where a nonce can't reach. Keep the nonce to `script-src`, since a nonce in `style-src` makes browsers ignore `'unsafe-inline'`:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
Rails.application.configure do
|
|
71
|
+
config.content_security_policy do |policy|
|
|
72
|
+
policy.script_src :self, "https://heydeacon.com"
|
|
73
|
+
policy.style_src :self, :unsafe_inline
|
|
74
|
+
policy.connect_src :self, "https://heydeacon.com"
|
|
75
|
+
policy.frame_src "https://heydeacon.com"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
|
|
79
|
+
config.content_security_policy_nonce_directives = %w(script-src)
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Opening the chat from your own button
|
|
84
|
+
|
|
85
|
+
The widget comes with its own button. To use one of yours instead, hide Deacon's in Appearance in the dashboard and call `window.Deacon.open()` from your button or link. `Deacon.close()` and `Deacon.toggle()` work too.
|
|
86
|
+
|
|
87
|
+
## What it adds
|
|
88
|
+
|
|
89
|
+
The helper prints the same tag Install gives you:
|
|
90
|
+
|
|
91
|
+
```html
|
|
92
|
+
<script src="https://heydeacon.com/widget/v1/loader.js" data-key="pk_your_widget_key" async></script>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
It loads in the background without holding up the page. Works with Rails 8.0 and later, on Ruby 3.3 and later.
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module Deacon
|
|
5
|
+
module Rails
|
|
6
|
+
# The view helper the layout calls once, in its head.
|
|
7
|
+
module Helper
|
|
8
|
+
LOADER_SRC = "https://heydeacon.com/widget/v1/loader.js".freeze
|
|
9
|
+
|
|
10
|
+
MISSING_KEY = "deacon-rails: the key is missing, so the page has no Deacon widget. Set " \
|
|
11
|
+
"DEACON_KEY or config.deacon.key. In Deacon, open Install (the button at the " \
|
|
12
|
+
"top of the dashboard) and copy the data-key value from the snippet. It " \
|
|
13
|
+
"starts with pk_.".freeze
|
|
14
|
+
|
|
15
|
+
# The tag Install gives, with the key from config.deacon.key, or from DEACON_KEY when
|
|
16
|
+
# the app sets none. With no key it prints nothing and logs a warning, so a missing
|
|
17
|
+
# setting never takes a page down.
|
|
18
|
+
def deacon_widget_tag
|
|
19
|
+
key = (deacon_config.key || ENV["DEACON_KEY"]).to_s.strip
|
|
20
|
+
|
|
21
|
+
if key.empty?
|
|
22
|
+
::Rails.logger&.warn(MISSING_KEY)
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context = deacon_config.context
|
|
27
|
+
page_views = deacon_config.page_views
|
|
28
|
+
deacon_check_context(context)
|
|
29
|
+
deacon_check_page_views(page_views)
|
|
30
|
+
|
|
31
|
+
attributes = { "data-key" => key }
|
|
32
|
+
attributes["data-context"] = JSON.generate(context) unless context.nil?
|
|
33
|
+
attributes["data-no-page-views"] = true if page_views == false
|
|
34
|
+
|
|
35
|
+
deacon_loader_tag(attributes, deacon_csp_nonce)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def deacon_config
|
|
41
|
+
::Rails.application.config.deacon
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The request's Content Security Policy nonce, when the app has a nonce generator and the
|
|
45
|
+
# page was rendered for a request.
|
|
46
|
+
def deacon_csp_nonce
|
|
47
|
+
content_security_policy_nonce if respond_to?(:content_security_policy_nonce)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# A wrong type is a mistake in the app's code, so it raises, as any view error does.
|
|
51
|
+
def deacon_check_context(context)
|
|
52
|
+
return if context.nil?
|
|
53
|
+
return if context.is_a?(Hash) && context.each_value.all? { |value| deacon_scalar?(value) }
|
|
54
|
+
|
|
55
|
+
raise ArgumentError, "deacon-rails: config.deacon.context must be a hash whose values " \
|
|
56
|
+
"are strings, numbers or booleans."
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def deacon_check_page_views(page_views)
|
|
60
|
+
return if page_views.nil? || page_views == true || page_views == false
|
|
61
|
+
|
|
62
|
+
raise ArgumentError, "deacon-rails: config.deacon.page_views must be true or false."
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def deacon_scalar?(value)
|
|
66
|
+
value.is_a?(String) || value.is_a?(Integer) || value.is_a?(Float) || value == true || value == false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The tag Install gives, with any of its other attributes between the key and async, and
|
|
70
|
+
# the nonce after them. An attribute that is true is written with no value, as async is.
|
|
71
|
+
def deacon_loader_tag(attributes, nonce)
|
|
72
|
+
rendered = attributes.map do |name, value|
|
|
73
|
+
value == true ? " #{name}" : %( #{name}="#{ERB::Util.html_escape(value)}")
|
|
74
|
+
end.join
|
|
75
|
+
|
|
76
|
+
nonce_attribute = nonce.nil? ? "" : %( nonce="#{ERB::Util.html_escape(nonce)}")
|
|
77
|
+
|
|
78
|
+
%(<script src="#{LOADER_SRC}"#{rendered} async#{nonce_attribute}></script>).html_safe
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Deacon
|
|
2
|
+
module Rails
|
|
3
|
+
# What config.deacon holds. A Struct rather than ordered options, so a misspelt setting
|
|
4
|
+
# fails loudly at boot instead of being ignored.
|
|
5
|
+
Configuration = Struct.new(:key, :context, :page_views)
|
|
6
|
+
|
|
7
|
+
# Adds config.deacon to the app and deacon_widget_tag to its views.
|
|
8
|
+
class Railtie < ::Rails::Railtie
|
|
9
|
+
config.deacon = Configuration.new
|
|
10
|
+
|
|
11
|
+
initializer "deacon.view_helper" do
|
|
12
|
+
ActiveSupport.on_load(:action_view) { include Deacon::Rails::Helper }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/deacon/rails.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: deacon-rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Deacon
|
|
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: actionview
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: railties
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '8.0'
|
|
40
|
+
description: A view helper, deacon_widget_tag, that puts the Deacon chat widget on
|
|
41
|
+
every page of a Rails app from one line in the layout. Deacon is an AI support agent
|
|
42
|
+
that answers questions from your own docs and content, and says when it doesn't
|
|
43
|
+
know.
|
|
44
|
+
email:
|
|
45
|
+
- hello@heydeacon.com
|
|
46
|
+
executables: []
|
|
47
|
+
extensions: []
|
|
48
|
+
extra_rdoc_files: []
|
|
49
|
+
files:
|
|
50
|
+
- LICENSE
|
|
51
|
+
- README.md
|
|
52
|
+
- lib/deacon/rails.rb
|
|
53
|
+
- lib/deacon/rails/helper.rb
|
|
54
|
+
- lib/deacon/rails/railtie.rb
|
|
55
|
+
- lib/deacon/rails/version.rb
|
|
56
|
+
homepage: https://heydeacon.com
|
|
57
|
+
licenses:
|
|
58
|
+
- MIT
|
|
59
|
+
metadata:
|
|
60
|
+
homepage_uri: https://heydeacon.com
|
|
61
|
+
source_code_uri: https://github.com/heydeacon/deacon-rails
|
|
62
|
+
bug_tracker_uri: https://github.com/heydeacon/deacon-rails/issues
|
|
63
|
+
rubygems_mfa_required: 'true'
|
|
64
|
+
rdoc_options: []
|
|
65
|
+
require_paths:
|
|
66
|
+
- lib
|
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '3.3'
|
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
requirements: []
|
|
78
|
+
rubygems_version: 3.6.9
|
|
79
|
+
specification_version: 4
|
|
80
|
+
summary: Adds Deacon, an AI support chat that answers from your docs, to every page
|
|
81
|
+
of a Rails app.
|
|
82
|
+
test_files: []
|