active_canvas 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/MIT-LICENSE +20 -0
- data/README.md +318 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/active_canvas/editor/ai_panel.js +1607 -0
- data/app/assets/javascripts/active_canvas/editor/asset_manager.js +498 -0
- data/app/assets/javascripts/active_canvas/editor/blocks.js +1083 -0
- data/app/assets/javascripts/active_canvas/editor/code_panel.js +572 -0
- data/app/assets/javascripts/active_canvas/editor/component_toolbar.js +394 -0
- data/app/assets/javascripts/active_canvas/editor/panels.js +460 -0
- data/app/assets/javascripts/active_canvas/editor/utils.js +56 -0
- data/app/assets/javascripts/active_canvas/editor.js +295 -0
- data/app/assets/stylesheets/active_canvas/application.css +15 -0
- data/app/assets/stylesheets/active_canvas/editor.css +2929 -0
- data/app/controllers/active_canvas/admin/ai_controller.rb +181 -0
- data/app/controllers/active_canvas/admin/application_controller.rb +56 -0
- data/app/controllers/active_canvas/admin/media_controller.rb +61 -0
- data/app/controllers/active_canvas/admin/page_types_controller.rb +57 -0
- data/app/controllers/active_canvas/admin/page_versions_controller.rb +23 -0
- data/app/controllers/active_canvas/admin/pages_controller.rb +133 -0
- data/app/controllers/active_canvas/admin/partials_controller.rb +88 -0
- data/app/controllers/active_canvas/admin/settings_controller.rb +256 -0
- data/app/controllers/active_canvas/application_controller.rb +20 -0
- data/app/controllers/active_canvas/pages_controller.rb +18 -0
- data/app/controllers/concerns/active_canvas/current_user.rb +12 -0
- data/app/controllers/concerns/active_canvas/rate_limitable.rb +75 -0
- data/app/controllers/concerns/active_canvas/tailwind_compilation.rb +39 -0
- data/app/helpers/active_canvas/application_helper.rb +4 -0
- data/app/jobs/active_canvas/application_job.rb +4 -0
- data/app/jobs/active_canvas/compile_tailwind_job.rb +64 -0
- data/app/mailers/active_canvas/application_mailer.rb +6 -0
- data/app/models/active_canvas/ai_model.rb +136 -0
- data/app/models/active_canvas/application_record.rb +5 -0
- data/app/models/active_canvas/media.rb +141 -0
- data/app/models/active_canvas/page.rb +85 -0
- data/app/models/active_canvas/page_type.rb +22 -0
- data/app/models/active_canvas/page_version.rb +80 -0
- data/app/models/active_canvas/partial.rb +73 -0
- data/app/models/active_canvas/setting.rb +292 -0
- data/app/services/active_canvas/ai_configuration.rb +40 -0
- data/app/services/active_canvas/ai_models.rb +128 -0
- data/app/services/active_canvas/ai_service.rb +289 -0
- data/app/services/active_canvas/content_sanitizer.rb +112 -0
- data/app/services/active_canvas/tailwind_compiler.rb +156 -0
- data/app/views/active_canvas/admin/media/index.html.erb +401 -0
- data/app/views/active_canvas/admin/media/show.html.erb +297 -0
- data/app/views/active_canvas/admin/page_types/_form.html.erb +25 -0
- data/app/views/active_canvas/admin/page_types/edit.html.erb +13 -0
- data/app/views/active_canvas/admin/page_types/index.html.erb +29 -0
- data/app/views/active_canvas/admin/page_types/new.html.erb +9 -0
- data/app/views/active_canvas/admin/page_types/show.html.erb +18 -0
- data/app/views/active_canvas/admin/page_versions/show.html.erb +469 -0
- data/app/views/active_canvas/admin/pages/_form.html.erb +62 -0
- data/app/views/active_canvas/admin/pages/content.html.erb +139 -0
- data/app/views/active_canvas/admin/pages/edit.html.erb +335 -0
- data/app/views/active_canvas/admin/pages/editor.html.erb +710 -0
- data/app/views/active_canvas/admin/pages/index.html.erb +149 -0
- data/app/views/active_canvas/admin/pages/new.html.erb +19 -0
- data/app/views/active_canvas/admin/pages/show.html.erb +258 -0
- data/app/views/active_canvas/admin/pages/versions.html.erb +333 -0
- data/app/views/active_canvas/admin/partials/edit.html.erb +182 -0
- data/app/views/active_canvas/admin/partials/editor.html.erb +703 -0
- data/app/views/active_canvas/admin/partials/index.html.erb +131 -0
- data/app/views/active_canvas/admin/settings/show.html.erb +1864 -0
- data/app/views/active_canvas/pages/no_homepage.html.erb +45 -0
- data/app/views/active_canvas/pages/show.html.erb +113 -0
- data/app/views/layouts/active_canvas/admin/application.html.erb +960 -0
- data/app/views/layouts/active_canvas/admin/editor.html.erb +826 -0
- data/app/views/layouts/active_canvas/application.html.erb +55 -0
- data/config/routes.rb +48 -0
- data/db/migrate/20260202000001_create_active_canvas_tables.rb +113 -0
- data/db/migrate/20260202000002_create_active_canvas_ai_models.rb +26 -0
- data/lib/active_canvas/configuration.rb +232 -0
- data/lib/active_canvas/engine.rb +44 -0
- data/lib/active_canvas/version.rb +3 -0
- data/lib/active_canvas.rb +26 -0
- data/lib/generators/active_canvas/install/install_generator.rb +263 -0
- data/lib/generators/active_canvas/install/templates/initializer.rb.tt +163 -0
- data/lib/tasks/active_canvas_tasks.rake +69 -0
- metadata +150 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d4b96c0042fa7c12334c67d92ab53a6a59766099111c0abbb854b386f95c69fa
|
|
4
|
+
data.tar.gz: 4c49e4bdcaea61447a43c4b2a4b7111904354f6f343a8a5ff17eaa08525ee0bd
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 177d5257bac30fe5da6e91cb5c65bd9584648a5173fbe7c654bbd2dd1f85f6b85d357d3a56cfc07dd3d6595a8ca7de71a8ff38f8f3ae5cc6e1215e0d7dcc50c7
|
|
7
|
+
data.tar.gz: e02cc03e5f88c357338b92e627e707c392fa9de486157925156d619b86061db8d3079a7b4cc2278e4969f37a4a12fa324e84140fa1d737844ed5e29b1fe7918c
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Giovanni Panasiti
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# ActiveCanvas
|
|
2
|
+
|
|
3
|
+
A mountable Rails engine that turns any Rails app into a full-featured CMS. Includes a visual drag-and-drop editor (GrapeJS), AI-powered content generation, Tailwind CSS compilation, media management, page versioning, and SEO controls -- all behind an admin interface that works out of the box.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Visual Editor** -- Drag-and-drop page builder powered by GrapeJS
|
|
8
|
+
- **AI Content Generation** -- Text, images, and screenshot-to-code via OpenAI, Anthropic, or OpenRouter
|
|
9
|
+
- **Tailwind CSS Compilation** -- Per-page compiled CSS for production (no CDN dependency)
|
|
10
|
+
- **Media Library** -- Upload and manage images/files with Active Storage
|
|
11
|
+
- **Page Versioning** -- Automatic version history with diffs and rollback
|
|
12
|
+
- **Header & Footer Partials** -- Reusable components, togglable per page
|
|
13
|
+
- **SEO** -- Meta tags, Open Graph, Twitter Cards, JSON-LD structured data
|
|
14
|
+
- **Page Types** -- Categorize pages (blog posts, landing pages, etc.)
|
|
15
|
+
- **Authentication** -- Pluggable auth (Devise, custom, or HTTP Basic)
|
|
16
|
+
- **Isolated Namespace** -- No conflicts with your host application
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Ruby 3.1+
|
|
21
|
+
- Rails 8.0+
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Add to your Gemfile:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
gem "active_canvas"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Run the install generator:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bundle install
|
|
35
|
+
bin/rails generate active_canvas:install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The interactive installer will:
|
|
39
|
+
- Copy and run database migrations
|
|
40
|
+
- Create `config/initializers/active_canvas.rb` with configuration options
|
|
41
|
+
- Mount the engine in your routes (default: `/canvas`)
|
|
42
|
+
- Prompt you to choose a CSS framework (Tailwind, Bootstrap 5, or none)
|
|
43
|
+
- Optionally configure AI API keys
|
|
44
|
+
|
|
45
|
+
Then visit `/canvas/admin` to start building pages.
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
1. Go to `/canvas/admin`
|
|
50
|
+
2. Create a **Page Type** (e.g., "Landing Page")
|
|
51
|
+
3. Create a **Page**, then click **Editor** to open the visual builder
|
|
52
|
+
4. Drag blocks, use AI to generate content, upload images
|
|
53
|
+
5. Publish the page -- it's live at `/canvas/your-slug`
|
|
54
|
+
|
|
55
|
+
## Visual Editor
|
|
56
|
+
|
|
57
|
+
The GrapeJS editor provides:
|
|
58
|
+
|
|
59
|
+
- Drag-and-drop blocks (text, images, columns, forms, etc.)
|
|
60
|
+
- Code editor panel for direct HTML/CSS editing
|
|
61
|
+
- Asset manager integrated with the media library
|
|
62
|
+
- AI assistant panel for content generation
|
|
63
|
+
- Component-level AI toolbar (edit, rewrite, expand)
|
|
64
|
+
- Auto-save (configurable interval, default: 60s)
|
|
65
|
+
|
|
66
|
+
## AI Integration
|
|
67
|
+
|
|
68
|
+
ActiveCanvas uses [RubyLLM](https://github.com/crmne/ruby_llm) to provide AI features directly in the editor.
|
|
69
|
+
|
|
70
|
+
### Capabilities
|
|
71
|
+
|
|
72
|
+
| Feature | Description | Supported Models |
|
|
73
|
+
|---------|-------------|-----------------|
|
|
74
|
+
| **Chat** | Generate and edit HTML content with streaming | GPT-4o, Claude Sonnet 4, Claude 3.5 Haiku |
|
|
75
|
+
| **Image Generation** | Create images from text prompts | DALL-E 3, GPT Image 1 |
|
|
76
|
+
| **Screenshot to Code** | Upload a screenshot, get HTML/CSS | GPT-4o, Claude Sonnet 4 (vision models) |
|
|
77
|
+
|
|
78
|
+
### Setup
|
|
79
|
+
|
|
80
|
+
Add your API keys via environment variables or Rails credentials:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# config/initializers/active_canvas.rb
|
|
84
|
+
Rails.application.config.after_initialize do
|
|
85
|
+
# Via environment variables
|
|
86
|
+
ActiveCanvas::Setting.ai_openai_api_key = ENV["OPENAI_API_KEY"]
|
|
87
|
+
ActiveCanvas::Setting.ai_anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
|
|
88
|
+
ActiveCanvas::Setting.ai_openrouter_api_key = ENV["OPENROUTER_API_KEY"]
|
|
89
|
+
|
|
90
|
+
# Or via Rails credentials
|
|
91
|
+
credentials = Rails.application.credentials.active_canvas || {}
|
|
92
|
+
ActiveCanvas::Setting.ai_openai_api_key = credentials[:openai_api_key]
|
|
93
|
+
end
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
You can also configure API keys from the admin UI at `/canvas/admin/settings` (AI tab).
|
|
97
|
+
|
|
98
|
+
Once configured, sync available models:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
bin/rails active_canvas:sync_models
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Or use the **Sync Models** button in admin settings.
|
|
105
|
+
|
|
106
|
+
### Server Timeout
|
|
107
|
+
|
|
108
|
+
AI requests (especially image generation and screenshot-to-code) can take longer than typical web requests. If you're running Puma in clustered mode (multiple workers), increase the worker timeout to avoid requests being killed mid-flight:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
# config/puma.rb
|
|
112
|
+
worker_timeout 180
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Or via environment variable:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
PUMA_WORKER_TIMEOUT=180
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
If you're behind a reverse proxy (Nginx, Apache), also increase its read timeout for the ActiveCanvas routes:
|
|
122
|
+
|
|
123
|
+
```nginx
|
|
124
|
+
location /canvas {
|
|
125
|
+
proxy_read_timeout 180s;
|
|
126
|
+
proxy_send_timeout 180s;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Tailwind CSS Compilation
|
|
131
|
+
|
|
132
|
+
ActiveCanvas can compile Tailwind CSS at runtime so your public pages don't need the Tailwind CDN.
|
|
133
|
+
|
|
134
|
+
### How it works
|
|
135
|
+
|
|
136
|
+
- **In the editor**: Uses Tailwind CDN for instant live preview
|
|
137
|
+
- **On save**: Compiles only the CSS classes used on that page
|
|
138
|
+
- **Public pages**: Serves compiled CSS inline (fast, no CDN)
|
|
139
|
+
|
|
140
|
+
### Setup
|
|
141
|
+
|
|
142
|
+
Add the `tailwindcss-ruby` gem (optional -- falls back to CDN if missing):
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
gem "tailwindcss-ruby", ">= 4.0"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Select "Tailwind CSS" as the CSS framework in your initializer or admin settings. That's it -- CSS compiles automatically when you save pages.
|
|
149
|
+
|
|
150
|
+
You can customize the Tailwind theme (colors, fonts) from admin settings, and trigger a bulk recompile of all pages when needed.
|
|
151
|
+
|
|
152
|
+
## Media Library
|
|
153
|
+
|
|
154
|
+
Upload and manage images directly from the admin or from within the editor's asset manager.
|
|
155
|
+
|
|
156
|
+
- Supports JPEG, PNG, GIF, WebP, AVIF, and PDF
|
|
157
|
+
- SVG uploads available (disabled by default for security)
|
|
158
|
+
- Configurable max file size (default: 10MB)
|
|
159
|
+
- Works with any Active Storage backend (local, S3, GCS, etc.)
|
|
160
|
+
- Public or signed URL modes
|
|
161
|
+
|
|
162
|
+
## Page Versioning
|
|
163
|
+
|
|
164
|
+
Every content change creates a version automatically. View the version history from the page admin to see:
|
|
165
|
+
|
|
166
|
+
- What changed (before/after diffs)
|
|
167
|
+
- Who made the change
|
|
168
|
+
- When it was made
|
|
169
|
+
- Content size differences
|
|
170
|
+
|
|
171
|
+
Configure the maximum versions kept per page (default: 50, set to 0 for unlimited).
|
|
172
|
+
|
|
173
|
+
## Authentication
|
|
174
|
+
|
|
175
|
+
**The admin interface is open by default.** Configure authentication before deploying to production.
|
|
176
|
+
|
|
177
|
+
### With Devise
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
ActiveCanvas.configure do |config|
|
|
181
|
+
config.authenticate_admin = :authenticate_user!
|
|
182
|
+
end
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### With a custom controller
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
ActiveCanvas.configure do |config|
|
|
189
|
+
config.admin_parent_controller = "Admin::ApplicationController"
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### With HTTP Basic Auth
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
ActiveCanvas.configure do |config|
|
|
197
|
+
config.authenticate_admin = :http_basic_auth
|
|
198
|
+
config.http_basic_user = "admin"
|
|
199
|
+
config.http_basic_password = Rails.application.credentials.active_canvas_password
|
|
200
|
+
end
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### With custom logic
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
ActiveCanvas.configure do |config|
|
|
207
|
+
config.authenticate_admin = -> {
|
|
208
|
+
unless current_user&.admin?
|
|
209
|
+
redirect_to main_app.root_path, alert: "Access denied"
|
|
210
|
+
end
|
|
211
|
+
}
|
|
212
|
+
end
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Configuration
|
|
216
|
+
|
|
217
|
+
Full configuration reference:
|
|
218
|
+
|
|
219
|
+
```ruby
|
|
220
|
+
# config/initializers/active_canvas.rb
|
|
221
|
+
ActiveCanvas.configure do |config|
|
|
222
|
+
# === Authentication ===
|
|
223
|
+
config.authenticate_admin = :authenticate_user! # method name, lambda, or :http_basic_auth
|
|
224
|
+
config.authenticate_public = nil # nil = public access
|
|
225
|
+
config.current_user_method = :current_user # for version tracking & AI features
|
|
226
|
+
|
|
227
|
+
# === CSS Framework ===
|
|
228
|
+
config.css_framework = :tailwind # :tailwind, :bootstrap5, or :none
|
|
229
|
+
|
|
230
|
+
# === Media Uploads ===
|
|
231
|
+
config.enable_uploads = true
|
|
232
|
+
config.max_upload_size = 10.megabytes
|
|
233
|
+
config.allow_svg_uploads = false
|
|
234
|
+
config.storage_service = nil # Active Storage service name
|
|
235
|
+
config.public_uploads = false # false = signed URLs
|
|
236
|
+
|
|
237
|
+
# === Editor ===
|
|
238
|
+
config.enable_ai_features = true
|
|
239
|
+
config.enable_code_editor = true
|
|
240
|
+
config.enable_asset_manager = true
|
|
241
|
+
config.autosave_interval = 60 # seconds (0 = disabled)
|
|
242
|
+
|
|
243
|
+
# === Pages ===
|
|
244
|
+
config.max_versions_per_page = 50 # 0 = unlimited
|
|
245
|
+
|
|
246
|
+
# === Security ===
|
|
247
|
+
config.sanitize_content = true
|
|
248
|
+
config.ai_rate_limit_per_minute = 30
|
|
249
|
+
end
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Customization
|
|
253
|
+
|
|
254
|
+
### Mount path
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
# config/routes.rb
|
|
258
|
+
mount ActiveCanvas::Engine => "/pages" # or "/cms", "/blog", "/"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Override views
|
|
262
|
+
|
|
263
|
+
Copy any view into your app to customize it:
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
app/views/active_canvas/pages/show.html.erb
|
|
267
|
+
app/views/active_canvas/admin/pages/index.html.erb
|
|
268
|
+
app/views/layouts/active_canvas/admin/application.html.erb
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Extend models
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
ActiveCanvas::Page.class_eval do
|
|
275
|
+
validates :content, presence: true
|
|
276
|
+
|
|
277
|
+
def excerpt
|
|
278
|
+
content.to_s.truncate(200)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Rake Tasks
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
bin/rails active_canvas:sync_models # Sync AI models from configured providers
|
|
287
|
+
bin/rails active_canvas:list_models # List all synced AI models
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Development
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
git clone https://github.com/giovapanasiti/active_canvas.git
|
|
294
|
+
cd active_canvas
|
|
295
|
+
bundle install
|
|
296
|
+
bin/rails db:migrate
|
|
297
|
+
bin/rails test
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Start the dummy app:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
bin/rails server
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Then visit `http://localhost:3000/canvas/admin`.
|
|
307
|
+
|
|
308
|
+
## Contributing
|
|
309
|
+
|
|
310
|
+
1. Fork the repository
|
|
311
|
+
2. Create your feature branch (`git checkout -b feature/my-feature`)
|
|
312
|
+
3. Commit your changes
|
|
313
|
+
4. Push to the branch (`git push origin feature/my-feature`)
|
|
314
|
+
5. Create a Pull Request
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|