ideasbugs 0.7.7 → 0.7.8

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: e054b5c1fcb3a26c77783e54d044550ca8a395bad3189540451b79498e36fe0b
4
- data.tar.gz: 567787250431c93054d02c0cca2a0366515138c35f9d2ac7a9ed87162d318d9f
3
+ metadata.gz: 9875f27cec0e4396ed1c2b269cea94335852c1a4776850fd0944bf9e52f5d154
4
+ data.tar.gz: c2301f7df468b8659b99cafb8e4f52e118b0f23f70c983359a1d236368e75121
5
5
  SHA512:
6
- metadata.gz: f8542b1c572317d929ae744c2caaeb2e0e59240ad68e740c290d3a15ab4761b9a55cf54b4332b6334667585f45603f9ce9444a3362ccc0a7762ab9c856ee330e
7
- data.tar.gz: 8da898919668edb4d3f7d87e76e4223a295bea3374c8e7a355b446277ed08b8983606aecb81544ccf765e1640d26c34f01febaf447bc1950b0e1aa89a771e7fc
6
+ metadata.gz: dd3615cbda1b4455a49a345ec5c0620a810547220a44ead6434c1a912f308fad963b4f022b5e9ea3c78140c72d33365420e43c893cb4330975d929ebd9ded36b
7
+ data.tar.gz: b29d7052738b06accc692290f3973c88ef7537fa26cdd5d728da46036267bc581310012600b18d1f20fe9113abd162f733f4b1045867f793f0ae9452ed0276ce
data/AGENTS.md ADDED
@@ -0,0 +1,174 @@
1
+ # AGENTS.md
2
+
3
+ Instructions for coding agents. Two audiences:
4
+
5
+ - **[Installing ideasbugs into a Rails app](#installing-into-a-rails-app)** — you are working in a host app and were asked to add product feedback, bug reports, or a feature-request board.
6
+ - **[Working on the gem itself](#working-on-the-gem-itself)** — you are working in this repository.
7
+
8
+ Requirements: Ruby >= 3.2, Rails >= 7.1. Active Storage only for screenshots. The widget needs the CSRF token from `csrf_meta_tags`, which a standard Rails layout already has.
9
+
10
+ If you are in a host app and this file is not in front of you, it ships inside the gem: `cat "$(bundle show ideasbugs)/AGENTS.md"`.
11
+
12
+ ---
13
+
14
+ ## Installing into a Rails app
15
+
16
+ ### 1. Install
17
+
18
+ ```bash
19
+ bundle add ideasbugs
20
+ bin/rails generate ideasbugs:install
21
+ bin/rails db:migrate
22
+ ```
23
+
24
+ The generator writes `config/initializers/ideasbugs.rb`, one migration (`ideasbugs_feedbacks`), and `mount_ideasbugs at: "/feedback"` into `config/routes.rb`. Note the mount path is **`/feedback`**, not `/ideasbugs`. Read the initializer it wrote — every option is documented there in comments, and it is the source of truth over any summary of it, including this file.
25
+
26
+ Every `config.…` line below belongs inside the `Ideasbugs.configure do |config|` block in that initializer. Uncomment and edit in place rather than appending a second `configure` block.
27
+
28
+ ### 2. Wire the three things the generator cannot
29
+
30
+ **a. The widget tag.** Nothing appears until this is on the page:
31
+
32
+ ```erb
33
+ <%# app/views/layouts/application.html.erb, before </body> %>
34
+ <%= ideasbugs_tag %>
35
+ ```
36
+
37
+ The helper is injected into ActionView by the engine — no include, no import, no asset pipeline entry. A floating **Feedback** button appears bottom-right.
38
+
39
+ **b. `authorize_admin` — do this before deploying.** The dashboard at `/feedback` defaults to **development only**. It fails closed, so shipping without this is not an open dashboard — it is a 403 reading "Forbidden. Set Ideasbugs.config.authorize_admin to grant access."
40
+
41
+ Note the asymmetry, and that it is deliberate: **`enabled` defaults to everyone** (real users in production are the point of feedback collection) while **`authorize_admin` defaults to nobody outside development**.
42
+
43
+ ```ruby
44
+ config.authorize_admin = ->(request) { request.env["warden"]&.user&.admin? }
45
+ ```
46
+
47
+ **c. Attribution**, if the app has users. Optional, but without it every submission is anonymous.
48
+
49
+ ```ruby
50
+ config.current_user = ->(request) { request.env["warden"]&.user }
51
+ config.author_label = ->(user) { user.email } # the short label stored + shown
52
+ ```
53
+
54
+ > **`enabled`, `authorize_admin`, `current_user` and `tenant` receive the raw `request`, not a controller.** Writing `->(request) { current_user }` is the most common mistake here — that method does not exist in this scope. Resolve the user *from the request*: Warden env, a signed cookie, `Current.user` if middleware already set it. `author_label` is the exception: it receives whatever `current_user` returned.
55
+
56
+ Rails 8 built-in auth:
57
+
58
+ ```ruby
59
+ config.current_user = lambda do |request|
60
+ token = request.cookies["session_token"]
61
+ Session.find_signed(token)&.user if token
62
+ end
63
+ ```
64
+
65
+ ### 3. Verify
66
+
67
+ ```bash
68
+ bin/rails routes | grep ideasbugs # engine mounted
69
+ bin/rails ideasbugs:seed_demo # optional sample feedback, idempotent
70
+ ```
71
+
72
+ Then in the running app: load any page, confirm the Feedback button appears, send one, and triage it at `/feedback`.
73
+
74
+ ### Shaping the widget
75
+
76
+ ```ruby
77
+ config.kinds = %w[bug feature other] # labels resolve through I18n (ideasbugs.kinds.<kind>)
78
+ config.sections = ["Billing", "Dashboard"] # [] hides the select entirely
79
+ config.show_button = false # then open it from your own UI
80
+ config.button_label = "Report a problem" # nil = localized default
81
+ ```
82
+
83
+ With `show_button = false`, any element carrying `data-ideasbugs-open` opens the form — put it in a menu, a footer, a help panel.
84
+
85
+ ### Screenshots
86
+
87
+ `config.screenshots` is on by default but **requires Active Storage in the host app** (`rails active_storage:install`); the widget hides the upload control when it is off or Active Storage is absent, and the config exposes `screenshots_enabled?` for exactly that pair of conditions. Caps: `max_screenshots` (3), `max_screenshot_size` (5 MB), both enforced server-side. Images stream through the dashboard's own gate at `/feedback/feedbacks/:id/screenshots/:id` — **never a public blob URL**. Do not build your own blob links.
88
+
89
+ ### Statuses
90
+
91
+ `open → in_review → resolved`, as plain strings in `Ideasbugs::Feedback::STATUSES` with a scope per status (`Feedback.open`, `.in_review`, `.resolved`) plus `newest_first`. Deliberately not an Active Record enum — `open` as an enum scope would collide with `Kernel#open`. Do not "modernize" it into an enum.
92
+
93
+ ### Multi-tenancy
94
+
95
+ One resolver returning an **opaque key** — GlobalID, id, subdomain, slug. The gem never takes a foreign key into host models:
96
+
97
+ ```ruby
98
+ config.tenant = ->(request) { Current.customer&.to_gid&.to_s }
99
+ ```
100
+
101
+ Optional sugar on a host model (`has_feedback` is available on every Active Record class already):
102
+
103
+ ```ruby
104
+ class Customer < ApplicationRecord
105
+ has_feedback # keyed by to_gid.to_s — must match config.tenant
106
+ end
107
+ customer.feedback.open
108
+ ```
109
+
110
+ `bin/rails generate ideasbugs:tenant` exists **only** to add the `tenant` column to installs made before it existed. A fresh install already has it, and running that generator will fail on a duplicate column. Do not run it as part of a new install.
111
+
112
+ ### Do not
113
+
114
+ - **Do not copy the widget JavaScript into `app/javascript`, or add a `<script>` tag for it.** `ideasbugs_tag` renders what is needed and the engine serves the code same-origin. There is no build step and nothing for esbuild/importmap/Tailwind to know about.
115
+ - **Do not build your own dashboard.** Use the mounted one; `config.admin_layout = "admin/application"` renders it inside an existing admin shell.
116
+ - **Do not expose screenshots by blob URL** — the gated route exists so a leaked signed URL cannot hand over a customer's screenshot.
117
+ - **Do not set config outside the initializer.** `rate_limit` in particular is read once when the controller class loads; assigning config per-request mutates it process-wide.
118
+ - **Do not convert the status strings to an enum** (see above).
119
+
120
+ ### Configuration worth knowing
121
+
122
+ Everything is optional; a fresh install works with zero config. Full list with comments is in the generated initializer.
123
+
124
+ | Option | Default | Note |
125
+ | --- | --- | --- |
126
+ | `authorize_admin` | development only | **Who can read the dashboard. Set before deploying.** |
127
+ | `enabled` | everyone | Per-request gate for the widget and submissions |
128
+ | `current_user` | `nil` | Receives the request |
129
+ | `author_label` | email, else `to_s` | Receives the user |
130
+ | `tenant` | `nil` | One board per tenant — see [Multi-tenancy](#multi-tenancy) |
131
+ | `kinds` | `bug feature other` | Labels via `ideasbugs.kinds.<kind>` |
132
+ | `sections` | `[]` | App areas as a select; empty hides it |
133
+ | `screenshots` | `true` | Needs Active Storage; inert without it |
134
+ | `max_screenshots`, `max_screenshot_size` | `3`, `5.megabytes` | Enforced server-side |
135
+ | `storage_service` | app default | A `storage.yml` key for a dedicated bucket |
136
+ | `show_button`, `button_label` | `true`, localized | `false` = open from `data-ideasbugs-open` |
137
+ | `admin_layout` | `ideasbugs/application` | Render inside your admin shell |
138
+ | `rate_limit` | `{ to: 10, within: 60 }` | Rails 7.2+; ignored on 7.1. `nil` disables |
139
+ | `mount_path` | `"/feedback"` | Keep in sync with `mount_ideasbugs at:` |
140
+ | `on_submit` | no-op | Runs inline after save — Slack, email, a ticket |
141
+
142
+ ### Common failure modes
143
+
144
+ | Symptom | Cause |
145
+ | --- | --- |
146
+ | `/feedback` returns 403 "Set Ideasbugs.config.authorize_admin to grant access" | Exactly what it says: still at the development-only default |
147
+ | No Feedback button | `ideasbugs_tag` missing from the rendered layout, `config.enabled` false, or `show_button = false` with no opener of your own |
148
+ | Submissions rejected with an invalid-token error | The layout is missing `csrf_meta_tags` |
149
+ | No screenshot upload control | Active Storage not installed, or `screenshots = false` |
150
+ | `ideasbugs:tenant` fails on a duplicate column | It is an upgrade generator for pre-tenant installs; a fresh install already has the column |
151
+ | `undefined local variable current_user` in the initializer | A gate lambda treated its argument as a controller. It is a `request` |
152
+
153
+ ---
154
+
155
+ ## Working on the gem itself
156
+
157
+ ```bash
158
+ bundle exec rake test # minitest, dummy app under test/dummy
159
+ bundle exec rake test:system # browser tests, separate task
160
+ bundle exec rubocop # must be clean
161
+ BUNDLE_GEMFILE=gemfiles/rails_7.1.gemfile bundle exec rake test # 7.1, 7.2, 8.0, 8.1 in gemfiles/
162
+ ```
163
+
164
+ Layout: `app/` controller, model, dashboard views · `lib/ideasbugs/` config, widget JS, seeds, engine, `has_feedback` · `lib/generators/ideasbugs/` install and tenant · `config/locales/` · `test/` minitest with `test/dummy` as the host app, system tests excluded from the default task.
165
+
166
+ Conventions this codebase holds to — follow them rather than the first thing that works:
167
+
168
+ - **Multi-tenancy is an opaque string key, never a foreign key.** `config.tenant` returns whatever the host wants; `has_feedback` is a veneer over `Feedback.for_tenant`. No association, no `owner_type` coupling.
169
+ - **Active Storage is optional at runtime.** `screenshots_enabled?` checks the switch *and* whether the constant is defined, so an app without Active Storage gets a working widget rather than an exception.
170
+ - **Attachments stream through the engine's gate**, never a public blob URL.
171
+ - **The widget is plain JS served same-origin by the engine** — no build step, no framework.
172
+ - **The dummy app pins `config.active_job.queue_adapter = :test`.** Do not remove it or let it drift back to the `:async` default. Attaching a screenshot enqueues Active Storage's analysis job, and `:async` runs it on a background thread that checks out its own connection — writes no test transaction covers, landing in the middle of whatever runs next. That is a suite that fails order-dependently in a test which never created a row, and it is miserable to trace back.
173
+ - Every user-facing change bumps `lib/ideasbugs/version.rb` and adds a `CHANGELOG.md` entry that says what it costs, not only what it adds.
174
+ - Commit messages are prose that explains the tradeoff — read `git log` before writing one.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.8
4
+
5
+ - Adds `AGENTS.md`: install and integration instructions written for coding
6
+ agents — the request-shaped config lambdas, the `/feedback` mount path, why
7
+ the status strings are not an enum, and the mistakes agents actually make. It
8
+ ships inside the gem, so `cat "$(bundle show ideasbugs)/AGENTS.md"` works from
9
+ a host app.
10
+ - The dummy app pins `queue_adapter = :test` for the test suite. Attaching a
11
+ screenshot enqueues Active Storage's analysis job, and the default `:async`
12
+ adapter runs it on a background thread with its own database connection —
13
+ writes no test transaction covers, which is how a suite starts failing
14
+ order-dependently in a test that never created a row. No effect on the gem
15
+ itself.
16
+
3
17
  ## 0.7.7 (2026-08-01)
4
18
 
5
19
  - Added `Ideasbugs::Seeds.load!` and a `rake ideasbugs:seed_demo` task that
data/README.md CHANGED
@@ -64,6 +64,11 @@ duplicating them.
64
64
  Ruby >= 3.2 · Rails >= 7.1 · Active Storage only if you want screenshots ·
65
65
  CSRF token comes from `csrf_meta_tags`, already in a standard Rails layout.
66
66
 
67
+ Installing with a coding agent? Point it at [AGENTS.md](AGENTS.md) — the same
68
+ steps in the order an agent needs them, plus the gates it tends to get wrong and
69
+ the things it should not do. It ships inside the gem, so
70
+ `cat "$(bundle show ideasbugs)/AGENTS.md"` works from any app that bundles it.
71
+
67
72
  ## What you get
68
73
 
69
74
  | | |
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ideasbugs
4
- VERSION = '0.7.7'
4
+ VERSION = '0.7.8'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ideasbugs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.7.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov
@@ -34,6 +34,7 @@ executables: []
34
34
  extensions: []
35
35
  extra_rdoc_files: []
36
36
  files:
37
+ - AGENTS.md
37
38
  - CHANGELOG.md
38
39
  - MIT-LICENSE
39
40
  - README.md