ideasbugs 0.5.4 → 0.6.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 +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +40 -2
- data/app/controllers/ideasbugs/application_controller.rb +9 -0
- data/app/controllers/ideasbugs/feedbacks_controller.rb +15 -3
- data/app/controllers/ideasbugs/screenshots_controller.rb +2 -1
- data/app/models/ideasbugs/feedback.rb +5 -0
- data/app/views/ideasbugs/feedbacks/index.html.erb +6 -2
- data/lib/generators/ideasbugs/install/templates/create_ideasbugs_feedbacks.rb.tt +2 -0
- data/lib/generators/ideasbugs/install/templates/initializer.rb +13 -0
- data/lib/generators/ideasbugs/tenant/templates/add_tenant_to_ideasbugs_feedbacks.rb.tt +8 -0
- data/lib/generators/ideasbugs/tenant/tenant_generator.rb +39 -0
- data/lib/ideasbugs/configuration.rb +10 -0
- data/lib/ideasbugs/engine.rb +8 -0
- data/lib/ideasbugs/has_feedback.rb +28 -0
- data/lib/ideasbugs/version.rb +1 -1
- data/lib/ideasbugs.rb +25 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6658dd5686d24377bb3d968e620282473b8b4769a02f11cfcc4ef5fbf2ba4ae9
|
|
4
|
+
data.tar.gz: a12d3f136d6c693f020a56b703b1c3bd050527e2045585dc1a080197977827bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b002bc9672adf86e27a2b59e62c88b745d4b6a1449777f78308627abc8894d92b9538191ff562e9b3cf271fed9adaa3ec0425ba05b9148505f3bab963112c59d
|
|
7
|
+
data.tar.gz: f05923a9dd619623f615c756261100e0c726b6a80e592dd1dc9ada985226f88e76ed12a5333eb67d375e4478e94e1f40a160df4f46b44bebc779b592e55e1a93
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 (2026-07-26)
|
|
4
|
+
|
|
5
|
+
- **Multi-tenancy.** Give each customer/tenant its own board — its own widget
|
|
6
|
+
submissions and its own triage dashboard — via one resolver:
|
|
7
|
+
`config.tenant = ->(request) { Current.organization&.to_gid&.to_s }`. The key
|
|
8
|
+
is an opaque string (GlobalID, id, subdomain, slug); the gem takes no foreign
|
|
9
|
+
key into your models, exactly like author attribution. Every submission is
|
|
10
|
+
stamped and the dashboard (plus screenshot access) scopes to the resolved
|
|
11
|
+
tenant. Authorization composes: `authorize_admin` says who's an admin,
|
|
12
|
+
`tenant` says which tenant, so a customer's admin sees only their own — and a
|
|
13
|
+
cross-tenant id 404s instead of leaking.
|
|
14
|
+
- Optional `has_feedback` model concern (veneer over the string key, no new
|
|
15
|
+
coupling): `customer.feedback.open` — plus `Ideasbugs.for(record)`.
|
|
16
|
+
- **Single-tenant apps are unchanged** — `config.tenant` defaults to nil, one
|
|
17
|
+
global board. Upgrading: the `tenant` column is additive and nullable; run
|
|
18
|
+
`bin/rails generate ideasbugs:tenant && bin/rails db:migrate` (fresh installs
|
|
19
|
+
already include it). Existing rows keep a nil tenant.
|
|
20
|
+
|
|
21
|
+
## 0.5.5 (2026-07-25)
|
|
22
|
+
|
|
23
|
+
- The dashboard's **Section** column now only appears when it carries
|
|
24
|
+
information — when sections are configured (`config.sections`) or some
|
|
25
|
+
record already has one. Apps that don't use sections no longer see a
|
|
26
|
+
permanently blank column.
|
|
27
|
+
|
|
3
28
|
## 0.5.4 (2026-07-25)
|
|
4
29
|
|
|
5
30
|
- Docs: show how to wire current_user with Rails 8's built-in authentication
|
data/README.md
CHANGED
|
@@ -302,10 +302,48 @@ end
|
|
|
302
302
|
```
|
|
303
303
|
|
|
304
304
|
Each row stores `kind`, `section`, `message`, `status` (`open`, `in_review`,
|
|
305
|
-
`resolved`), `page_url`, `user_agent`,
|
|
306
|
-
`author_label`. Screenshots are Active Storage attachments
|
|
305
|
+
`resolved`), `page_url`, `user_agent`, an optional `tenant`, and optional
|
|
306
|
+
`author_id` / `author_label`. Screenshots are Active Storage attachments
|
|
307
307
|
(`feedback.screenshots`).
|
|
308
308
|
|
|
309
|
+
## Multi-tenancy
|
|
310
|
+
|
|
311
|
+
Give each customer/tenant its own board — its own widget submissions and its
|
|
312
|
+
own triage dashboard — with one resolver:
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
config.tenant = ->(request) { Current.organization&.to_gid&.to_s }
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Return an **opaque key** — a GlobalID, an id, a subdomain, a slug. The gem
|
|
319
|
+
never takes a foreign key into your models and never needs to know what a
|
|
320
|
+
Customer is; it just stamps the key on each submission and scopes the dashboard
|
|
321
|
+
(and screenshot access) to it. `nil` (the default) is a single global board —
|
|
322
|
+
single-tenant apps need none of this and keep working unchanged.
|
|
323
|
+
|
|
324
|
+
Authorization composes: your `authorize_admin` says *who* is an admin, the
|
|
325
|
+
`tenant` resolver says *which* tenant they're in, so a customer's admin sees
|
|
326
|
+
only their own board — and a cross-tenant id 404s.
|
|
327
|
+
|
|
328
|
+
Optional model sugar — a veneer over the string key, not a new coupling:
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
class Customer < ApplicationRecord
|
|
332
|
+
has_feedback # keyed by to_gid.to_s (match config.tenant)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
customer.feedback.open # a normal Active Record relation
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Apps that skip the concern still get full multi-tenancy from the resolver
|
|
339
|
+
alone. Key by something other than GlobalID? Pass a matching resolver to both:
|
|
340
|
+
`has_feedback(key: ->(c){ c.subdomain })` and the same in `config.tenant`.
|
|
341
|
+
|
|
342
|
+
**Upgrading an existing install:** the `tenant` column is additive and
|
|
343
|
+
nullable. Run `bin/rails generate ideasbugs:tenant && bin/rails db:migrate`
|
|
344
|
+
(a fresh install already includes it). Existing rows keep a `nil` tenant — the
|
|
345
|
+
global board — so nothing changes until you set `config.tenant`.
|
|
346
|
+
|
|
309
347
|
## Security
|
|
310
348
|
|
|
311
349
|
- Submission and dashboard access are both gated **on the server** for every
|
|
@@ -11,5 +11,14 @@ module Ideasbugs
|
|
|
11
11
|
|
|
12
12
|
@current_author = Ideasbugs.config.current_user.call(request)
|
|
13
13
|
end
|
|
14
|
+
|
|
15
|
+
# The tenant for this request (nil = the single global board). Every read
|
|
16
|
+
# and write scopes to it, so a resolved-tenant admin only ever sees and
|
|
17
|
+
# writes their own tenant's feedback.
|
|
18
|
+
def current_tenant
|
|
19
|
+
return @current_tenant if defined?(@current_tenant)
|
|
20
|
+
|
|
21
|
+
@current_tenant = Ideasbugs.tenant(request)
|
|
22
|
+
end
|
|
14
23
|
end
|
|
15
24
|
end
|
|
@@ -26,15 +26,20 @@ module Ideasbugs
|
|
|
26
26
|
@status = Feedback::STATUSES.include?(params[:status]) ? params[:status] : 'open'
|
|
27
27
|
@kind = Ideasbugs.config.kinds.map(&:to_s).include?(params[:kind]) ? params[:kind] : nil
|
|
28
28
|
@query = params[:q].to_s.strip.presence
|
|
29
|
-
@counts =
|
|
29
|
+
@counts = tenant_scope.group(:status).count
|
|
30
30
|
|
|
31
|
-
scope =
|
|
31
|
+
scope = tenant_scope.where(status: @status)
|
|
32
32
|
scope = scope.where(kind: @kind) if @kind
|
|
33
33
|
scope = search(scope) if @query
|
|
34
34
|
@page = [params[:page].to_i, 1].max
|
|
35
35
|
@feedbacks = scope.newest_first.offset((@page - 1) * PER_PAGE).limit(PER_PAGE + 1).to_a
|
|
36
36
|
@more = @feedbacks.size > PER_PAGE
|
|
37
37
|
@feedbacks = @feedbacks.first(PER_PAGE)
|
|
38
|
+
|
|
39
|
+
# Only surface the Section column when it can carry information: the host
|
|
40
|
+
# configured sections, or some record already has one (e.g. sections were
|
|
41
|
+
# configured historically). Otherwise it's a permanently blank column.
|
|
42
|
+
@show_section = Ideasbugs.config.sections.any? || @feedbacks.any? { |f| f.section.present? }
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
def show; end
|
|
@@ -42,6 +47,7 @@ module Ideasbugs
|
|
|
42
47
|
def create
|
|
43
48
|
feedback = Feedback.new(feedback_params)
|
|
44
49
|
feedback.user_agent = request.user_agent
|
|
50
|
+
feedback.tenant = current_tenant
|
|
45
51
|
attribute_author(feedback)
|
|
46
52
|
|
|
47
53
|
error = attach_screenshots(feedback)
|
|
@@ -80,7 +86,13 @@ module Ideasbugs
|
|
|
80
86
|
end
|
|
81
87
|
|
|
82
88
|
def set_feedback
|
|
83
|
-
@feedback =
|
|
89
|
+
@feedback = tenant_scope.find(params[:id])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Every dashboard query starts here, so an admin can only ever load,
|
|
93
|
+
# triage, or delete feedback in their own tenant — a cross-tenant id 404s.
|
|
94
|
+
def tenant_scope
|
|
95
|
+
Feedback.for_tenant(current_tenant)
|
|
84
96
|
end
|
|
85
97
|
|
|
86
98
|
# Case-insensitive match on the free-text columns. LOWER() keeps it
|
|
@@ -10,7 +10,8 @@ module Ideasbugs
|
|
|
10
10
|
before_action :require_admin
|
|
11
11
|
|
|
12
12
|
def show
|
|
13
|
-
screenshot = Feedback.
|
|
13
|
+
screenshot = Feedback.for_tenant(current_tenant)
|
|
14
|
+
.find(params[:feedback_id]).screenshots.find(params[:id])
|
|
14
15
|
|
|
15
16
|
send_data screenshot.download,
|
|
16
17
|
filename: screenshot.filename.to_s,
|
|
@@ -17,6 +17,11 @@ module Ideasbugs
|
|
|
17
17
|
presence: true,
|
|
18
18
|
inclusion: { in: ->(_) { Ideasbugs.config.kinds.map(&:to_s) } }
|
|
19
19
|
|
|
20
|
+
# Multi-tenancy: everything is scoped to an opaque tenant key (nil = the
|
|
21
|
+
# single global board). Loose-coupled like author_id — a string, no FK
|
|
22
|
+
# into the host's tables. See Ideasbugs.config.tenant.
|
|
23
|
+
scope :for_tenant, ->(tenant) { where(tenant: tenant.presence) }
|
|
24
|
+
|
|
20
25
|
scope :newest_first, -> { order(id: :desc) }
|
|
21
26
|
|
|
22
27
|
STATUSES.each do |status|
|
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
<tr>
|
|
34
34
|
<th><%= t('ideasbugs.kind', default: 'Type') %></th>
|
|
35
35
|
<th><%= t('ideasbugs.dashboard.message', default: 'Message') %></th>
|
|
36
|
-
|
|
36
|
+
<% if @show_section %>
|
|
37
|
+
<th><%= t('ideasbugs.section', default: 'Section') %></th>
|
|
38
|
+
<% end %>
|
|
37
39
|
<th><%= t('ideasbugs.dashboard.from', default: 'From') %></th>
|
|
38
40
|
<th><%= t('ideasbugs.dashboard.filed', default: 'Filed') %></th>
|
|
39
41
|
</tr>
|
|
@@ -52,7 +54,9 @@
|
|
|
52
54
|
<div class="muted">📎 <%= feedback.screenshots.count %></div>
|
|
53
55
|
<% end %>
|
|
54
56
|
</td>
|
|
55
|
-
|
|
57
|
+
<% if @show_section %>
|
|
58
|
+
<td><%= feedback.section %></td>
|
|
59
|
+
<% end %>
|
|
56
60
|
<td><%= feedback.author_label %></td>
|
|
57
61
|
<td class="muted" title="<%= feedback.created_at %>">
|
|
58
62
|
<%= time_ago_in_words(feedback.created_at) %>
|
|
@@ -11,11 +11,13 @@ class CreateIdeasbugsFeedbacks < ActiveRecord::Migration<%= migration_version %>
|
|
|
11
11
|
t.string :user_agent
|
|
12
12
|
t.string :author_id
|
|
13
13
|
t.string :author_label
|
|
14
|
+
t.string :tenant # opaque per-tenant key; nil = single global board
|
|
14
15
|
|
|
15
16
|
t.timestamps
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
add_index :ideasbugs_feedbacks, :status
|
|
19
20
|
add_index :ideasbugs_feedbacks, :kind
|
|
21
|
+
add_index :ideasbugs_feedbacks, %i[tenant status]
|
|
20
22
|
end
|
|
21
23
|
end
|
|
@@ -23,6 +23,19 @@ Ideasbugs.configure do |config|
|
|
|
23
23
|
# Label stored for the author and shown in the dashboard.
|
|
24
24
|
# config.author_label = ->(user) { user.try(:email) }
|
|
25
25
|
|
|
26
|
+
# Multi-tenancy (optional). Give each customer/tenant its own board — its own
|
|
27
|
+
# widget submissions and its own triage dashboard. Return an opaque key
|
|
28
|
+
# (nil = one global board, the default). A GlobalID is the recommended key
|
|
29
|
+
# and matches the `has_feedback` model concern; an id, subdomain or slug work
|
|
30
|
+
# too. The gem never needs to know what your tenant is. An admin then sees
|
|
31
|
+
# only their tenant's board.
|
|
32
|
+
# config.tenant = ->(request) { Current.organization&.to_gid&.to_s }
|
|
33
|
+
#
|
|
34
|
+
# Then, optionally, `has_feedback` for `customer.feedback`:
|
|
35
|
+
# class Customer < ApplicationRecord
|
|
36
|
+
# has_feedback
|
|
37
|
+
# end
|
|
38
|
+
|
|
26
39
|
# Feedback types users can pick from. Labels come from I18n
|
|
27
40
|
# (ideasbugs.kinds.<kind>).
|
|
28
41
|
# config.kinds = %w[bug feature other]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'rails/generators/active_record'
|
|
5
|
+
|
|
6
|
+
module Ideasbugs
|
|
7
|
+
module Generators
|
|
8
|
+
# For apps that installed ideasbugs before multi-tenancy: adds the nullable
|
|
9
|
+
# `tenant` column (and its index) to ideasbugs_feedbacks. A fresh install
|
|
10
|
+
# already gets it via the install generator — run this one only to upgrade
|
|
11
|
+
# an existing install. Additive and safe: existing rows keep a nil tenant
|
|
12
|
+
# (the single global board), so nothing changes until you set config.tenant.
|
|
13
|
+
#
|
|
14
|
+
# bin/rails generate ideasbugs:tenant && bin/rails db:migrate
|
|
15
|
+
class TenantGenerator < Rails::Generators::Base
|
|
16
|
+
include ActiveRecord::Generators::Migration
|
|
17
|
+
|
|
18
|
+
source_root File.expand_path('templates', __dir__)
|
|
19
|
+
|
|
20
|
+
desc 'Adds the tenant column to ideasbugs_feedbacks (multi-tenancy upgrade).'
|
|
21
|
+
|
|
22
|
+
def create_migration_file
|
|
23
|
+
migration_template 'add_tenant_to_ideasbugs_feedbacks.rb.tt',
|
|
24
|
+
'db/migrate/add_tenant_to_ideasbugs_feedbacks.rb'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def post_install
|
|
28
|
+
say "\ntenant column queued. Run `rails db:migrate`, then set", :green
|
|
29
|
+
say 'config.tenant in config/initializers/ideasbugs.rb to scope per tenant.'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def migration_version
|
|
35
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -20,6 +20,15 @@ module Ideasbugs
|
|
|
20
20
|
# responding to #id, or nil. Receives the request.
|
|
21
21
|
attr_accessor :current_user
|
|
22
22
|
|
|
23
|
+
# Resolve the current tenant (optional, for multi-tenant apps). Return an
|
|
24
|
+
# opaque key — a GlobalID, an id, a subdomain, a slug — or nil. Receives
|
|
25
|
+
# the request; shaped exactly like current_user/authorize_admin. nil (the
|
|
26
|
+
# default) is a single, global board: today's behavior, unchanged. Each
|
|
27
|
+
# submission is stamped with it and the dashboard scopes to it, so every
|
|
28
|
+
# tenant gets its own board. The recommended key is a GlobalID
|
|
29
|
+
# (`record.to_gid.to_s`), which also matches the `has_feedback` concern.
|
|
30
|
+
attr_accessor :tenant
|
|
31
|
+
|
|
23
32
|
# Turn a resolved user into a short label stored alongside the feedback and
|
|
24
33
|
# shown in the dashboard. Receives whatever #current_user returned.
|
|
25
34
|
attr_accessor :author_label
|
|
@@ -67,6 +76,7 @@ module Ideasbugs
|
|
|
67
76
|
@enabled = ->(_request) { true }
|
|
68
77
|
@authorize_admin = ->(_request) { Rails.env.development? }
|
|
69
78
|
@current_user = ->(_request) {}
|
|
79
|
+
@tenant = ->(_request) {}
|
|
70
80
|
@author_label = ->(user) { user.respond_to?(:email) ? user.email : user&.to_s }
|
|
71
81
|
@kinds = %w[bug feature other]
|
|
72
82
|
@sections = []
|
data/lib/ideasbugs/engine.rb
CHANGED
|
@@ -9,5 +9,13 @@ module Ideasbugs
|
|
|
9
9
|
include Ideasbugs::WidgetHelper
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
|
+
|
|
13
|
+
# Make the optional `has_feedback` model macro available on every Active
|
|
14
|
+
# Record model, without requiring the host to include anything.
|
|
15
|
+
initializer 'ideasbugs.model' do
|
|
16
|
+
ActiveSupport.on_load(:active_record) do
|
|
17
|
+
extend Ideasbugs::HasFeedback
|
|
18
|
+
end
|
|
19
|
+
end
|
|
12
20
|
end
|
|
13
21
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ideasbugs
|
|
4
|
+
# Optional sugar for multi-tenant apps. `has_feedback` on a host model gives
|
|
5
|
+
# it a `.feedback` relation, keyed by the record's GlobalID — the documented
|
|
6
|
+
# tenant convention:
|
|
7
|
+
#
|
|
8
|
+
# class Customer < ApplicationRecord
|
|
9
|
+
# has_feedback
|
|
10
|
+
# end
|
|
11
|
+
# customer.feedback.open # a normal Active Record relation
|
|
12
|
+
#
|
|
13
|
+
# It's a pure veneer over `Feedback.for_tenant(...)` — no association, no
|
|
14
|
+
# foreign key, no `owner_type` class-name coupling. Apps that don't want it
|
|
15
|
+
# still get full multi-tenancy through `config.tenant` alone.
|
|
16
|
+
#
|
|
17
|
+
# The write side lines up automatically when the tenant resolver produces the
|
|
18
|
+
# same GlobalID key, e.g. `config.tenant = ->(r){ Current.customer.to_gid.to_s }`.
|
|
19
|
+
# Key by something else? Pass a matching resolver: `has_feedback(key:
|
|
20
|
+
# ->(c){ c.subdomain })` — and use the same in `config.tenant`.
|
|
21
|
+
module HasFeedback
|
|
22
|
+
def has_feedback(as: :feedback, key: nil) # rubocop:disable Naming/PredicatePrefix
|
|
23
|
+
resolver = key || ->(record) { record.to_gid.to_s }
|
|
24
|
+
|
|
25
|
+
define_method(as) { Ideasbugs::Feedback.for_tenant(resolver.call(self)) }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/ideasbugs/version.rb
CHANGED
data/lib/ideasbugs.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'ideasbugs/version'
|
|
4
4
|
require 'ideasbugs/configuration'
|
|
5
5
|
require 'ideasbugs/widget'
|
|
6
|
+
require 'ideasbugs/has_feedback'
|
|
6
7
|
require 'ideasbugs/engine'
|
|
7
8
|
|
|
8
9
|
# In-app product feedback collection for Rails. A drop-in widget lets users
|
|
@@ -30,5 +31,29 @@ module Ideasbugs
|
|
|
30
31
|
def admin?(request)
|
|
31
32
|
!!config.authorize_admin.call(request)
|
|
32
33
|
end
|
|
34
|
+
|
|
35
|
+
# The tenant key for this request, or nil for the single global board.
|
|
36
|
+
# Normalized to a string so `where(tenant:)` is consistent whether the
|
|
37
|
+
# resolver returns a GlobalID, an integer id, or a slug; blank becomes nil.
|
|
38
|
+
def tenant(request)
|
|
39
|
+
config.tenant.call(request).presence&.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The feedback belonging to a host record, keyed by its GlobalID — the
|
|
43
|
+
# documented tenant convention. Sugar for `Feedback.for_tenant(...)`; the
|
|
44
|
+
# `has_feedback` model concern is built on this.
|
|
45
|
+
def for(record)
|
|
46
|
+
Feedback.for_tenant(tenant_key_for(record))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# A host record's tenant key: its GlobalID string. Kept in one place so
|
|
52
|
+
# `.for` and `has_feedback` agree — and so a host wiring
|
|
53
|
+
# `config.tenant = ->(r){ Current.org.to_gid.to_s }` lines up with
|
|
54
|
+
# `organization.feedback`.
|
|
55
|
+
def tenant_key_for(record)
|
|
56
|
+
record.respond_to?(:to_gid) ? record.to_gid.to_s : record.to_s
|
|
57
|
+
end
|
|
33
58
|
end
|
|
34
59
|
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.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Shmarov
|
|
@@ -78,9 +78,12 @@ files:
|
|
|
78
78
|
- lib/generators/ideasbugs/install/install_generator.rb
|
|
79
79
|
- lib/generators/ideasbugs/install/templates/create_ideasbugs_feedbacks.rb.tt
|
|
80
80
|
- lib/generators/ideasbugs/install/templates/initializer.rb
|
|
81
|
+
- lib/generators/ideasbugs/tenant/templates/add_tenant_to_ideasbugs_feedbacks.rb.tt
|
|
82
|
+
- lib/generators/ideasbugs/tenant/tenant_generator.rb
|
|
81
83
|
- lib/ideasbugs.rb
|
|
82
84
|
- lib/ideasbugs/configuration.rb
|
|
83
85
|
- lib/ideasbugs/engine.rb
|
|
86
|
+
- lib/ideasbugs/has_feedback.rb
|
|
84
87
|
- lib/ideasbugs/version.rb
|
|
85
88
|
- lib/ideasbugs/widget.js
|
|
86
89
|
- lib/ideasbugs/widget.rb
|