biscuit-rails 0.1.3 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7054c18a5049310cc03f684c2d8ce5c2a835276ffc2ac5c7fb7ad453a0683336
4
- data.tar.gz: 90b033ac9021dde624c9be3da900d3dedfb2697858ccafa536c28ba976f33a70
3
+ metadata.gz: a99734c304d99da013a29fd797e8d8d9461bdea8811bb87997bddc4a0b1cd100
4
+ data.tar.gz: b93c476239e0e9ee02f5466596b1de11dc3f24fcb3794c7b99497a66073efaf4
5
5
  SHA512:
6
- metadata.gz: 438ed681f247bfcde15c397a53f1765ee984e799abf8634af90308a2b3dc64b12ca222965bbca5b3bee49b8428e1a57ec0b3f25d97f98e9bcf7ed46e4d17483d
7
- data.tar.gz: 9c1f5f8363f50d9da83f8a82184725dbeed20b98073146437d2d2443036b504db365f77a2551b2ed40cff2505e4d5cd89bc19a45268d0a6af571a994d3f774aa
6
+ metadata.gz: 695482883b254ce859513919089eaa30f80947d792f9755cba738db35aff1bfde4bf6472cdbde5860b4e300acf05dda113ba595f26a422f2d6d60ef1833975d1
7
+ data.tar.gz: 197a2f37232f33b7b179fef7418497eaed80a3630016175e75709315e01778b273fdc0fa8347371b07c84884b0d78b0e696b9d6f0093eb930f779d8bc4ed0c6b
data/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2026-03-31
9
+
10
+ ### Added
11
+
12
+ - `rails generate biscuit:install` generator — installs a Claude Code skill
13
+ into `.claude/skills/biscuit-install/` for AI-assisted setup
14
+ - `biscuit-install` Claude Code skill — guides developers through compatibility
15
+ checks, engine mounting, Stimulus registration, initializer configuration,
16
+ cookie and tracking script audit, integration tests, and optional commit
17
+ - README section documenting the AI-assisted setup workflow
18
+
19
+ ---
20
+
21
+ ## [0.1.4] - 2026-03-29
22
+
23
+ ### Fixed
24
+
25
+ - Incorrect CSS float on banner element causing layout issues
26
+ - Incorrect banner height at small viewport widths
27
+
28
+ ---
29
+
8
30
  ## [0.1.3] - 2026-03-28
9
31
 
10
32
  ### Fixed
data/README.md CHANGED
@@ -21,7 +21,39 @@ default).
21
21
 
22
22
  ---
23
23
 
24
- ## Installation
24
+ ## AI-assisted setup (Claude Code)
25
+
26
+ If you use [Claude Code](https://claude.ai/code), the quickest way to install
27
+ and configure biscuit is via the built-in setup skill.
28
+
29
+ After adding the gem to your Gemfile and running `bundle install`, run the
30
+ generator to install the skill:
31
+
32
+ ```sh
33
+ rails generate biscuit:install
34
+ ```
35
+
36
+ Then open Claude Code in your project and run:
37
+
38
+ ```
39
+ /biscuit-install
40
+ ```
41
+
42
+ The skill will:
43
+
44
+ - Check your app is compatible (Ruby, Rails, Propshaft, Stimulus)
45
+ - Mount the engine and register the Stimulus controller
46
+ - Ask about banner position, cookie categories, and other preferences
47
+ - Generate `config/initializers/biscuit.rb` from your answers
48
+ - Scan your codebase for existing cookies and third-party tracking scripts
49
+ (Google Analytics, GTM, Meta Pixel, etc.) and help you wrap them with
50
+ `biscuit_allowed?` guards
51
+ - Add integration tests and run them
52
+ - Optionally commit everything
53
+
54
+ ---
55
+
56
+ ## Manual installation
25
57
 
26
58
  Add to your `Gemfile`:
27
59
 
@@ -26,12 +26,12 @@
26
26
  padding: var(--biscuit-padding);
27
27
  }
28
28
 
29
- .biscuit-banner[data-biscuit-position-value="bottom"] {
29
+ [data-biscuit-position-value="bottom"] .biscuit-banner {
30
30
  bottom: 0;
31
31
  box-shadow: var(--biscuit-shadow-bottom);
32
32
  }
33
33
 
34
- .biscuit-banner[data-biscuit-position-value="top"] {
34
+ [data-biscuit-position-value="top"] .biscuit-banner {
35
35
  top: 0;
36
36
  box-shadow: var(--biscuit-shadow-top);
37
37
  }
@@ -118,6 +118,7 @@
118
118
 
119
119
  @media (max-width: 640px) {
120
120
  .biscuit-banner__inner { flex-direction: column; align-items: flex-start; }
121
+ .biscuit-banner__message { flex-basis: auto; }
121
122
  .biscuit-banner__actions { width: 100%; }
122
123
  .biscuit-btn { flex: 1 1 auto; justify-content: center; }
123
124
  }
@@ -1,3 +1,3 @@
1
1
  module Biscuit
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,26 @@
1
+ require "rails/generators"
2
+
3
+ module Biscuit
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ desc "Installs the biscuit-rails Claude Code setup skill into .claude/skills/biscuit-install/"
9
+
10
+ def copy_claude_skill
11
+ empty_directory ".claude/skills/biscuit-install"
12
+ copy_file "SKILL.md", ".claude/skills/biscuit-install/SKILL.md"
13
+
14
+ say ""
15
+ say " Claude Code skill installed at .claude/skills/biscuit-install/SKILL.md", :green
16
+ say ""
17
+ say " Open Claude Code in this project and run:", :cyan
18
+ say " /biscuit-install", :cyan
19
+ say ""
20
+ say " The skill will check compatibility, configure the gem, audit your existing", :cyan
21
+ say " cookies and tracking scripts, add tests, and optionally commit.", :cyan
22
+ say ""
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,266 @@
1
+ ---
2
+ name: biscuit-install
3
+ description: Install and configure the biscuit-rails GDPR cookie consent gem. Checks compatibility, installs the gem, configures categories and position, audits existing cookies and tracking scripts, adds integration tests, and optionally commits.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # biscuit-install
8
+
9
+ Complete installation and setup of biscuit-rails in this Rails app. Work through each step in order, confirming with the user before making any changes.
10
+
11
+ ---
12
+
13
+ ## Step 1 — Compatibility check
14
+
15
+ Read `Gemfile`, `Gemfile.lock`, and `.ruby-version` (if present) to check:
16
+
17
+ - **Ruby >= 3.2** — stop with a clear error if not met
18
+ - **Rails >= 8.0** — check `Gemfile.lock` for the rails version; stop if not met
19
+ - **Asset pipeline** — check Gemfile for `propshaft`. If absent, warn that `stylesheet_link_tag "biscuit/biscuit"` may need adjustment
20
+ - **JS setup** — check for `importmap-rails` vs `jsbundling-rails`/`vite_rails`. Note the difference for Step 5
21
+ - **Stimulus** — check for `stimulus-rails` or `hotwire-rails`. Warn if absent — Stimulus must be installed for the banner to work
22
+ - **Conflicts** — check for other cookie consent gems (`eu_cookie_law`, `cookieconsent`, `cookie_law`). Warn if found
23
+
24
+ Report findings before proceeding.
25
+
26
+ ---
27
+
28
+ ## Step 2 — Add gem to Gemfile
29
+
30
+ Check if `biscuit-rails` already appears in the Gemfile. If not:
31
+
32
+ - Add `gem "biscuit-rails"` to the Gemfile
33
+ - Run `bundle install`
34
+
35
+ If already present, skip this step.
36
+
37
+ ---
38
+
39
+ ## Step 3 — Mount the engine
40
+
41
+ Check `config/routes.rb` for an existing `mount Biscuit::Engine` line. If absent, add it inside the `routes.draw` block:
42
+
43
+ ```ruby
44
+ mount Biscuit::Engine, at: "/biscuit"
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Step 4 — Configure the initializer
50
+
51
+ Ask the user the following questions before writing anything:
52
+
53
+ 1. **Banner position** — top or bottom? (default: `bottom`)
54
+ 2. **Cookie categories** — which categories beyond `necessary` are needed? Options: `analytics`, `marketing`, `preferences`, or custom names. (default: `analytics` and `marketing`)
55
+ 3. **Reload on consent** — should the page reload after the user saves consent, so conditionally-loaded scripts activate immediately? (default: `false`)
56
+ 4. **Privacy policy URL** — where does your privacy policy live? (default: `"/privacy"`)
57
+ 5. **Cookie lifetime** — how many days should consent last? (default: `365`)
58
+
59
+ Then create `config/initializers/biscuit.rb` based on their answers. Example output:
60
+
61
+ ```ruby
62
+ Biscuit.configure do |config|
63
+ config.position = :bottom
64
+ config.privacy_policy_url = "/privacy"
65
+ config.cookie_expires_days = 365
66
+
67
+ config.categories = {
68
+ necessary: { required: true },
69
+ analytics: { required: false },
70
+ marketing: { required: false }
71
+ }
72
+ end
73
+ ```
74
+
75
+ If `config/initializers/biscuit.rb` already exists, show the current content and ask before overwriting.
76
+
77
+ If custom category names are used, remind the user to add matching i18n keys to their locale files — for example:
78
+
79
+ ```yaml
80
+ en:
81
+ biscuit:
82
+ categories:
83
+ my_category:
84
+ name: "My Category"
85
+ description: "Used for..."
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Step 5 — Register the Stimulus controller
91
+
92
+ ### If using importmap
93
+
94
+ Check `app/javascript/controllers/index.js`. If `biscuit/biscuit_controller` is not already imported, add:
95
+
96
+ ```javascript
97
+ import BiscuitController from "biscuit/biscuit_controller"
98
+ application.register("biscuit", BiscuitController)
99
+ ```
100
+
101
+ Ensure `application` is already imported at the top of that file (it will be in the standard Rails 8 scaffold).
102
+
103
+ ### If using esbuild / jsbundling
104
+
105
+ The same import and register lines apply, but inform the user that `@hotwired/stimulus` must be marked as external in their esbuild config so it is not bundled twice:
106
+
107
+ ```js
108
+ // esbuild.config.js
109
+ external: ["@hotwired/stimulus"]
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Step 6 — Add stylesheet and banner to layout
115
+
116
+ Check `app/views/layouts/application.html.erb`:
117
+
118
+ - Add `<%= stylesheet_link_tag "biscuit/biscuit" %>` inside `<head>` if not already present
119
+ - Add `<%= biscuit_banner %>` as the first child of `<body>` if not already present
120
+
121
+ If the layout file doesn't exist at that path, ask the user which layout file to modify.
122
+
123
+ If the app uses `reload_on_consent: true` (from Step 4), use:
124
+
125
+ ```erb
126
+ <%= biscuit_banner(reload_on_consent: true) %>
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Step 7 — Cookie and tracking script audit
132
+
133
+ Scan the codebase for existing cookie and tracking usage that should be gated behind consent. Report all findings before making any changes — let the user decide what to wrap.
134
+
135
+ ### Server-side cookies (Ruby)
136
+
137
+ Search `app/` for:
138
+ - `cookies[`, `cookies.permanent[`, `cookies.signed[`, `cookies.encrypted[`
139
+
140
+ For each result, determine whether it is a functional/necessary cookie (session, CSRF, etc.) or a tracking cookie. Non-necessary cookies should be wrapped in the relevant controller:
141
+
142
+ ```ruby
143
+ if Biscuit::Consent.new(cookies).allowed?(:analytics)
144
+ cookies[:my_tracking_cookie] = { value: "...", expires: 1.year }
145
+ end
146
+ ```
147
+
148
+ ### Client-side storage (JavaScript)
149
+
150
+ Search `app/assets/` and `app/javascript/` for:
151
+ - `document.cookie`
152
+ - `localStorage.setItem`
153
+ - `sessionStorage.setItem`
154
+
155
+ Non-necessary writes should check the `biscuit_consent` cookie before setting.
156
+
157
+ ### Third-party scripts (layout/views)
158
+
159
+ Search `app/views/layouts/` and `app/views/` for known analytics and marketing patterns:
160
+
161
+ | Pattern | Category |
162
+ |---|---|
163
+ | `gtag(`, `googletagmanager.com`, `_gaq`, `ga(` | analytics |
164
+ | `GTM-` | analytics |
165
+ | `fbq(`, `connect.facebook.net` | marketing |
166
+ | `intercomSettings`, `widget.intercom.io` | marketing |
167
+ | `hj(`, `static.hotjar.com` | analytics |
168
+ | `hs-script-loader` | marketing |
169
+ | `_linkedin_data_partner_id` | marketing |
170
+
171
+ For each match, show the file, line number, and suggested wrapping:
172
+
173
+ ```erb
174
+ <% if biscuit_allowed?(:analytics) %>
175
+ <!-- existing script -->
176
+ <% end %>
177
+ ```
178
+
179
+ Ask the user to confirm each wrapping before applying it.
180
+
181
+ ---
182
+
183
+ ## Step 8 — Add integration tests
184
+
185
+ Check whether the app uses Minitest (`test/`) or RSpec (`spec/`).
186
+
187
+ ### Minitest
188
+
189
+ If `test/integration/biscuit_consent_test.rb` does not exist, create it:
190
+
191
+ ```ruby
192
+ require "test_helper"
193
+
194
+ class BiscuitConsentTest < ActionDispatch::IntegrationTest
195
+ test "banner is shown on first visit" do
196
+ get "/"
197
+ assert_response :success
198
+ assert_select "[data-controller='biscuit']"
199
+ end
200
+
201
+ test "biscuit_allowed? returns false before consent" do
202
+ get "/"
203
+ assert_equal false, Biscuit::Consent.new(cookies).allowed?(:analytics)
204
+ end
205
+
206
+ test "biscuit_allowed? for necessary always returns true" do
207
+ get "/"
208
+ assert_equal true, Biscuit::Consent.new(cookies).allowed?(:necessary)
209
+ end
210
+
211
+ test "accept all sets consent cookie" do
212
+ post "/biscuit/consent", params: {},
213
+ headers: { "Content-Type" => "application/json" },
214
+ as: :json,
215
+ body: { categories: { analytics: true, marketing: true } }.to_json
216
+ assert_response :success
217
+ assert Biscuit::Consent.new(cookies).given?
218
+ assert Biscuit::Consent.new(cookies).allowed?(:analytics)
219
+ end
220
+
221
+ test "reject all sets non-required categories to false" do
222
+ post "/biscuit/consent", params: {},
223
+ headers: { "Content-Type" => "application/json" },
224
+ as: :json,
225
+ body: { categories: { analytics: false, marketing: false } }.to_json
226
+ assert_response :success
227
+ assert Biscuit::Consent.new(cookies).given?
228
+ assert_equal false, Biscuit::Consent.new(cookies).allowed?(:analytics)
229
+ end
230
+ end
231
+ ```
232
+
233
+ Adjust category names to match the categories configured in Step 4.
234
+
235
+ ### RSpec
236
+
237
+ If `spec/requests/biscuit_consent_spec.rb` does not exist, create an equivalent using RSpec/Rails request spec syntax.
238
+
239
+ Run the new tests and confirm they pass before continuing.
240
+
241
+ ---
242
+
243
+ ## Step 9 — Optional commit
244
+
245
+ Ask the user: "Would you like to commit these changes?"
246
+
247
+ If yes, stage only the files that were created or modified during this setup and commit:
248
+
249
+ ```
250
+ git add config/routes.rb config/initializers/biscuit.rb \
251
+ app/views/layouts/application.html.erb \
252
+ app/javascript/controllers/index.js \
253
+ test/integration/biscuit_consent_test.rb
254
+ git commit -m "Install biscuit-rails cookie consent"
255
+ ```
256
+
257
+ Do not use `git add -A` — only stage biscuit-related files.
258
+
259
+ ---
260
+
261
+ ## Summary
262
+
263
+ After all steps complete, print a summary of:
264
+ - What was installed and configured
265
+ - Which tracking scripts were wrapped (if any)
266
+ - Any manual steps remaining (custom i18n keys, esbuild config, layouts other than `application.html.erb`)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: biscuit-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gareth James
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-03-28 00:00:00.000000000 Z
10
+ date: 2026-03-31 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -51,6 +51,8 @@ files:
51
51
  - lib/biscuit/engine.rb
52
52
  - lib/biscuit/rails.rb
53
53
  - lib/biscuit/version.rb
54
+ - lib/generators/biscuit/install/install_generator.rb
55
+ - lib/generators/biscuit/install/templates/SKILL.md
54
56
  homepage: https://github.com/garethfr/biscuit-rails
55
57
  licenses:
56
58
  - MIT