testimonials 0.1.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/CHANGELOG.md +8 -0
- data/MIT-LICENSE +20 -0
- data/README.md +175 -0
- data/Rakefile +11 -0
- data/app/controllers/testimonials/api/base_controller.rb +30 -0
- data/app/controllers/testimonials/api/stats_controller.rb +20 -0
- data/app/controllers/testimonials/api/testimonials_controller.rb +41 -0
- data/app/controllers/testimonials/application_controller.rb +50 -0
- data/app/controllers/testimonials/collection_controller.rb +18 -0
- data/app/controllers/testimonials/events_controller.rb +25 -0
- data/app/controllers/testimonials/media_controller.rb +48 -0
- data/app/controllers/testimonials/nps_controller.rb +71 -0
- data/app/controllers/testimonials/nps_responses_controller.rb +25 -0
- data/app/controllers/testimonials/testimonials_controller.rb +194 -0
- data/app/controllers/testimonials/widgets_controller.rb +36 -0
- data/app/helpers/testimonials/widget_helper.rb +83 -0
- data/app/models/testimonials/application_record.rb +7 -0
- data/app/models/testimonials/nps_response.rb +24 -0
- data/app/models/testimonials/prompt_event.rb +60 -0
- data/app/models/testimonials/testimonial.rb +53 -0
- data/app/views/layouts/testimonials/application.html.erb +118 -0
- data/app/views/layouts/testimonials/collection.html.erb +23 -0
- data/app/views/testimonials/collection/show.html.erb +12 -0
- data/app/views/testimonials/nps_responses/index.html.erb +67 -0
- data/app/views/testimonials/testimonials/index.html.erb +108 -0
- data/app/views/testimonials/testimonials/show.html.erb +105 -0
- data/config/locales/testimonials.ar.yml +101 -0
- data/config/locales/testimonials.bg.yml +101 -0
- data/config/locales/testimonials.bn.yml +101 -0
- data/config/locales/testimonials.de.yml +101 -0
- data/config/locales/testimonials.el.yml +101 -0
- data/config/locales/testimonials.en.yml +101 -0
- data/config/locales/testimonials.es.yml +101 -0
- data/config/locales/testimonials.fr.yml +101 -0
- data/config/locales/testimonials.hi.yml +101 -0
- data/config/locales/testimonials.hr.yml +101 -0
- data/config/locales/testimonials.id.yml +101 -0
- data/config/locales/testimonials.it.yml +101 -0
- data/config/locales/testimonials.ja.yml +101 -0
- data/config/locales/testimonials.ko.yml +101 -0
- data/config/locales/testimonials.lb.yml +101 -0
- data/config/locales/testimonials.nl.yml +101 -0
- data/config/locales/testimonials.pl.yml +101 -0
- data/config/locales/testimonials.pt.yml +101 -0
- data/config/locales/testimonials.ro.yml +101 -0
- data/config/locales/testimonials.ru.yml +101 -0
- data/config/locales/testimonials.th.yml +101 -0
- data/config/locales/testimonials.tr.yml +101 -0
- data/config/locales/testimonials.uk.yml +101 -0
- data/config/locales/testimonials.ur.yml +101 -0
- data/config/locales/testimonials.vi.yml +101 -0
- data/config/locales/testimonials.zh-CN.yml +101 -0
- data/config/routes.rb +29 -0
- data/lib/generators/testimonials/install/install_generator.rb +42 -0
- data/lib/generators/testimonials/install/templates/create_testimonials_tables.rb.tt +53 -0
- data/lib/generators/testimonials/install/templates/initializer.rb +70 -0
- data/lib/testimonials/configuration.rb +129 -0
- data/lib/testimonials/dashboard.js +33 -0
- data/lib/testimonials/engine.rb +16 -0
- data/lib/testimonials/prompt_helper.rb +15 -0
- data/lib/testimonials/version.rb +5 -0
- data/lib/testimonials/widget.js +1045 -0
- data/lib/testimonials/widget.rb +170 -0
- data/lib/testimonials.rb +69 -0
- metadata +132 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'digest'
|
|
5
|
+
|
|
6
|
+
module Testimonials
|
|
7
|
+
# Serves the self-contained browser widget. The JavaScript is plain ES (no
|
|
8
|
+
# framework, no build step) and styles itself inline, so it drops into any
|
|
9
|
+
# Rails app regardless of its CSS or JS setup. It is inlined into the page
|
|
10
|
+
# rather than served as a separate asset to avoid any dependency on the
|
|
11
|
+
# host's asset pipeline — and it lives under lib/ (not app/assets/) so a
|
|
12
|
+
# host that *does* run a pipeline never ingests it either.
|
|
13
|
+
module Widget
|
|
14
|
+
SOURCE = File.expand_path('widget.js', __dir__)
|
|
15
|
+
DASHBOARD_SOURCE = File.expand_path('dashboard.js', __dir__)
|
|
16
|
+
|
|
17
|
+
# Right-to-left scripts, so the form renders mirrored for those locales.
|
|
18
|
+
# Matched on the language subtag, so region variants ("ar-EG") count too.
|
|
19
|
+
RTL_LANGUAGES = %w[ar arc ckb dv fa ha he ks ku ps sd ug ur yi].freeze
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def javascript
|
|
23
|
+
@javascript ||= File.read(SOURCE)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def dashboard_javascript
|
|
27
|
+
@dashboard_javascript ||= File.read(DASHBOARD_SOURCE)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Content fingerprints for cache-busting script URLs: a changed file is
|
|
31
|
+
# a changed URL, so no browser can ever run stale widget code — Safari
|
|
32
|
+
# has been caught ignoring must-revalidate on same-URL scripts.
|
|
33
|
+
def fingerprint
|
|
34
|
+
@fingerprint ||= Digest::MD5.hexdigest(javascript)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def dashboard_fingerprint
|
|
38
|
+
@dashboard_fingerprint ||= Digest::MD5.hexdigest(dashboard_javascript)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The two <script> tags the helper renders.
|
|
42
|
+
#
|
|
43
|
+
# The config rides in a `type="application/json"` block: it is *data*,
|
|
44
|
+
# not code, so the browser never executes it and Turbo never tries to
|
|
45
|
+
# re-run it on a soft visit — which means it needs no CSP nonce and the
|
|
46
|
+
# widget can re-read the *current* page's config on every `turbo:load`.
|
|
47
|
+
#
|
|
48
|
+
# The code is a same-origin `src` script served by the engine — NOT
|
|
49
|
+
# inlined. Under a nonce-based CSP, Turbo Drive body swaps re-run body
|
|
50
|
+
# scripts against the *original* page's CSP header, so a fresh inline
|
|
51
|
+
# nonce gets refused; a same-origin src is covered by `'self'` on every
|
|
52
|
+
# visit. `nonce:` is still stamped for hosts whose script-src has no
|
|
53
|
+
# 'self'; pass nil when the app has no nonce.
|
|
54
|
+
def snippet(locale:, authenticated:, auto_open: nil, mode: 'widget', existing: nil, nonce: nil)
|
|
55
|
+
json = config_json(locale:, authenticated:, auto_open:, mode:, existing:)
|
|
56
|
+
nonce_attr = nonce ? %( nonce="#{nonce}") : ''
|
|
57
|
+
src = "#{Testimonials.config.mount_path.chomp('/')}/widget.js?v=#{fingerprint}"
|
|
58
|
+
|
|
59
|
+
%(<script type="application/json" data-testimonials-config>#{json}</script>) +
|
|
60
|
+
%(<script src="#{src}" defer#{nonce_attr} data-testimonials-widget></script>)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def config_json(locale:, authenticated:, auto_open:, mode:, existing: nil)
|
|
64
|
+
config = Testimonials.config
|
|
65
|
+
payload = {
|
|
66
|
+
endpoints: {
|
|
67
|
+
testimonials: config.testimonials_endpoint,
|
|
68
|
+
nps: config.nps_endpoint,
|
|
69
|
+
events: config.events_endpoint
|
|
70
|
+
},
|
|
71
|
+
locale: locale.to_s,
|
|
72
|
+
rtl: rtl?(locale),
|
|
73
|
+
authenticated: authenticated ? true : false,
|
|
74
|
+
autoOpen: auto_open&.to_s,
|
|
75
|
+
mode: mode.to_s,
|
|
76
|
+
questions: Testimonials.questions,
|
|
77
|
+
consent: Testimonials.consent_text,
|
|
78
|
+
# The signed-in user's current review, if any: the widget opens it
|
|
79
|
+
# pre-filled for editing instead of starting a second one — rating,
|
|
80
|
+
# text, consent state, and a playable URL for an attached video.
|
|
81
|
+
existing: existing && {
|
|
82
|
+
rating: existing.rating,
|
|
83
|
+
body: existing.body,
|
|
84
|
+
consent: existing.consent_given ? true : false,
|
|
85
|
+
videoUrl: existing_video_url(existing)
|
|
86
|
+
},
|
|
87
|
+
video: {
|
|
88
|
+
enabled: config.video_enabled?,
|
|
89
|
+
maxSeconds: config.max_video_seconds.to_i,
|
|
90
|
+
maxSize: config.max_video_size.to_i
|
|
91
|
+
},
|
|
92
|
+
avatars: {
|
|
93
|
+
enabled: config.avatars_enabled?,
|
|
94
|
+
maxSize: config.max_avatar_size.to_i
|
|
95
|
+
},
|
|
96
|
+
nps: { enabled: config.nps ? true : false },
|
|
97
|
+
labels: labels
|
|
98
|
+
}
|
|
99
|
+
# Escape "</" so a value can't close the <script> block early.
|
|
100
|
+
payload.to_json.gsub('</', '<\/')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def existing_video_url(existing)
|
|
106
|
+
return unless existing.video_attached?
|
|
107
|
+
|
|
108
|
+
"#{Testimonials.config.mount_path.chomp('/')}/#{existing.id}/video"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Every user-facing string in the widget, resolved through Rails I18n so
|
|
112
|
+
# the form follows the app's current locale. Each lookup carries an
|
|
113
|
+
# English default, so the widget stays fully worded even when a key is
|
|
114
|
+
# missing for the active locale.
|
|
115
|
+
def labels
|
|
116
|
+
app = Testimonials.app_name
|
|
117
|
+
{
|
|
118
|
+
enjoying: t(:enjoying, 'Enjoying %{app}?', app: app),
|
|
119
|
+
notNow: t(:not_now, 'Not now'),
|
|
120
|
+
rateAria: t(:rate_aria, 'Rate %{count} of 5'),
|
|
121
|
+
shareTitle: t(:share_title, 'Share your experience'),
|
|
122
|
+
updateTitle: t(:update_title, 'Update your review'),
|
|
123
|
+
promoterTitle: t(:promoter_title, 'Glad to hear it! Mind saying that publicly?'),
|
|
124
|
+
questionsTitle: t(:questions_title, 'Questions'),
|
|
125
|
+
message: t(:message, 'Your testimonial'),
|
|
126
|
+
messagePlaceholder: t(:message_placeholder, 'What would you tell a friend about %{app}?', app: app),
|
|
127
|
+
recordVideo: t(:record_video, 'Record a video'),
|
|
128
|
+
recordVideoHint: t(:record_video_hint, 'A short video says more than a page of text.'),
|
|
129
|
+
name: t(:name, 'Your name'),
|
|
130
|
+
email: t(:email, 'Your email'),
|
|
131
|
+
titleCompany: t(:title_company, 'Title, company'),
|
|
132
|
+
photo: t(:photo, 'Your photo'),
|
|
133
|
+
optional: t(:optional, 'optional'),
|
|
134
|
+
submit: t(:submit, 'Send'),
|
|
135
|
+
cancel: t(:cancel, 'Cancel'),
|
|
136
|
+
close: t(:close, 'Close'),
|
|
137
|
+
thanks: t(:thanks, 'Thank you so much!'),
|
|
138
|
+
videoCheckTitle: t(:video_check_title, 'Camera check'),
|
|
139
|
+
videoHint: t(:video_hint, 'Up to %{seconds} seconds. You can review it before sending.',
|
|
140
|
+
seconds: Testimonials.config.max_video_seconds.to_i),
|
|
141
|
+
startRecording: t(:start_recording, 'Start recording'),
|
|
142
|
+
stopRecording: t(:stop_recording, 'Finish'),
|
|
143
|
+
recordAgain: t(:record_again, 'Record again'),
|
|
144
|
+
useVideo: t(:use_video, 'Use this video'),
|
|
145
|
+
uploadInstead: t(:upload_instead, 'Upload a video file instead'),
|
|
146
|
+
videoAttached: t(:video_attached, 'Video attached'),
|
|
147
|
+
remove: t(:remove, 'Remove'),
|
|
148
|
+
npsQuestion: t(:nps_question, 'How likely are you to recommend %{app} to a friend or colleague?', app: app),
|
|
149
|
+
npsLow: t(:nps_low, 'Not at all likely'),
|
|
150
|
+
npsHigh: t(:nps_high, 'Extremely likely'),
|
|
151
|
+
npsCommentLabel: t(:nps_comment_label, "What's the main reason for your score?"),
|
|
152
|
+
errorBlank: t(:error_blank, 'Please write something or record a video.'),
|
|
153
|
+
errorContact: t(:error_contact, 'Please enter your name and email.'),
|
|
154
|
+
errorSave: t(:error_save, 'Could not send. Please try again.'),
|
|
155
|
+
errorVideoTooLarge: t(:error_video_too_large, 'The video is too large (max %{size} MB).',
|
|
156
|
+
size: Testimonials.config.max_video_size.to_i / (1024 * 1024)),
|
|
157
|
+
errorCamera: t(:error_camera, 'Could not access the camera. You can upload a video file instead.')
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def t(key, default, **args)
|
|
162
|
+
I18n.t(key, scope: :testimonials, default: default, **args)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def rtl?(locale)
|
|
166
|
+
RTL_LANGUAGES.include?(locale.to_s.downcase.split(/[-_]/).first)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
data/lib/testimonials.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'testimonials/version'
|
|
4
|
+
require 'testimonials/configuration'
|
|
5
|
+
require 'testimonials/widget'
|
|
6
|
+
require 'testimonials/prompt_helper'
|
|
7
|
+
require 'testimonials/engine'
|
|
8
|
+
|
|
9
|
+
# Testimonials, reviews and NPS for Rails. An iOS-style in-app widget collects
|
|
10
|
+
# star ratings and testimonials (text or video) at moments your code chooses;
|
|
11
|
+
# a public page collects them from shareable links; NPS promoters are routed
|
|
12
|
+
# straight into the testimonial ask. Everything lands in your own database
|
|
13
|
+
# with a minimal dashboard to approve, feature, and pick best lines — and a read API
|
|
14
|
+
# to render approved testimonials wherever you like.
|
|
15
|
+
module Testimonials
|
|
16
|
+
class << self
|
|
17
|
+
def config
|
|
18
|
+
@config ||= Configuration.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def configure
|
|
22
|
+
yield config
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Can this request see the widget and submit? Checked on the server for
|
|
26
|
+
# every public endpoint and by the helper before rendering.
|
|
27
|
+
def enabled?(request)
|
|
28
|
+
!!config.enabled.call(request)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Can this request browse and triage submissions? Checked by every
|
|
32
|
+
# dashboard action.
|
|
33
|
+
def admin?(request)
|
|
34
|
+
!!config.authorize_admin.call(request)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def app_name
|
|
38
|
+
config.app_name.presence || rails_app_name
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The guiding questions for the current locale, with %{app} filled in.
|
|
42
|
+
# I18n leaves the token alone when no interpolation values are passed,
|
|
43
|
+
# so a plain gsub covers built-in, literal, and lambda-provided strings
|
|
44
|
+
# alike.
|
|
45
|
+
def questions
|
|
46
|
+
list = config.questions
|
|
47
|
+
list = list.call if list.respond_to?(:call)
|
|
48
|
+
list = I18n.t('testimonials.questions', default: []) if list.nil?
|
|
49
|
+
Array(list).map { |q| q.to_s.gsub('%{app}', app_name) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def consent_text
|
|
53
|
+
config.consent_text.presence ||
|
|
54
|
+
I18n.t('testimonials.consent',
|
|
55
|
+
default: 'I give permission to use this testimonial across social channels ' \
|
|
56
|
+
'and other marketing efforts.')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# The application's module name, verbatim ("EthicsPortal", "SupeRails") —
|
|
62
|
+
# no inflection games. Set config.app_name for anything fancier.
|
|
63
|
+
def rails_app_name
|
|
64
|
+
Rails.application.class.module_parent_name
|
|
65
|
+
rescue StandardError
|
|
66
|
+
'this app'
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: testimonials
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yaroslav Shmarov
|
|
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: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.1'
|
|
26
|
+
description: |
|
|
27
|
+
A mountable Rails engine that collects customer testimonials — text and
|
|
28
|
+
video — and NPS scores from inside your app. An iOS-style "Enjoying this
|
|
29
|
+
app?" widget prompts users at moments your code chooses (with built-in
|
|
30
|
+
throttling), a public page collects testimonials from a shareable link,
|
|
31
|
+
and a minimal dashboard lets you approve, feature, and excerpt what comes
|
|
32
|
+
in. Display is headless: approved testimonials are served through your
|
|
33
|
+
models or an optional JSON API, and you render them with your own markup.
|
|
34
|
+
Framework-agnostic: no CSS or JS framework required.
|
|
35
|
+
email:
|
|
36
|
+
- yaroslav.shmarov@clickfunnels.com
|
|
37
|
+
executables: []
|
|
38
|
+
extensions: []
|
|
39
|
+
extra_rdoc_files: []
|
|
40
|
+
files:
|
|
41
|
+
- CHANGELOG.md
|
|
42
|
+
- MIT-LICENSE
|
|
43
|
+
- README.md
|
|
44
|
+
- Rakefile
|
|
45
|
+
- app/controllers/testimonials/api/base_controller.rb
|
|
46
|
+
- app/controllers/testimonials/api/stats_controller.rb
|
|
47
|
+
- app/controllers/testimonials/api/testimonials_controller.rb
|
|
48
|
+
- app/controllers/testimonials/application_controller.rb
|
|
49
|
+
- app/controllers/testimonials/collection_controller.rb
|
|
50
|
+
- app/controllers/testimonials/events_controller.rb
|
|
51
|
+
- app/controllers/testimonials/media_controller.rb
|
|
52
|
+
- app/controllers/testimonials/nps_controller.rb
|
|
53
|
+
- app/controllers/testimonials/nps_responses_controller.rb
|
|
54
|
+
- app/controllers/testimonials/testimonials_controller.rb
|
|
55
|
+
- app/controllers/testimonials/widgets_controller.rb
|
|
56
|
+
- app/helpers/testimonials/widget_helper.rb
|
|
57
|
+
- app/models/testimonials/application_record.rb
|
|
58
|
+
- app/models/testimonials/nps_response.rb
|
|
59
|
+
- app/models/testimonials/prompt_event.rb
|
|
60
|
+
- app/models/testimonials/testimonial.rb
|
|
61
|
+
- app/views/layouts/testimonials/application.html.erb
|
|
62
|
+
- app/views/layouts/testimonials/collection.html.erb
|
|
63
|
+
- app/views/testimonials/collection/show.html.erb
|
|
64
|
+
- app/views/testimonials/nps_responses/index.html.erb
|
|
65
|
+
- app/views/testimonials/testimonials/index.html.erb
|
|
66
|
+
- app/views/testimonials/testimonials/show.html.erb
|
|
67
|
+
- config/locales/testimonials.ar.yml
|
|
68
|
+
- config/locales/testimonials.bg.yml
|
|
69
|
+
- config/locales/testimonials.bn.yml
|
|
70
|
+
- config/locales/testimonials.de.yml
|
|
71
|
+
- config/locales/testimonials.el.yml
|
|
72
|
+
- config/locales/testimonials.en.yml
|
|
73
|
+
- config/locales/testimonials.es.yml
|
|
74
|
+
- config/locales/testimonials.fr.yml
|
|
75
|
+
- config/locales/testimonials.hi.yml
|
|
76
|
+
- config/locales/testimonials.hr.yml
|
|
77
|
+
- config/locales/testimonials.id.yml
|
|
78
|
+
- config/locales/testimonials.it.yml
|
|
79
|
+
- config/locales/testimonials.ja.yml
|
|
80
|
+
- config/locales/testimonials.ko.yml
|
|
81
|
+
- config/locales/testimonials.lb.yml
|
|
82
|
+
- config/locales/testimonials.nl.yml
|
|
83
|
+
- config/locales/testimonials.pl.yml
|
|
84
|
+
- config/locales/testimonials.pt.yml
|
|
85
|
+
- config/locales/testimonials.ro.yml
|
|
86
|
+
- config/locales/testimonials.ru.yml
|
|
87
|
+
- config/locales/testimonials.th.yml
|
|
88
|
+
- config/locales/testimonials.tr.yml
|
|
89
|
+
- config/locales/testimonials.uk.yml
|
|
90
|
+
- config/locales/testimonials.ur.yml
|
|
91
|
+
- config/locales/testimonials.vi.yml
|
|
92
|
+
- config/locales/testimonials.zh-CN.yml
|
|
93
|
+
- config/routes.rb
|
|
94
|
+
- lib/generators/testimonials/install/install_generator.rb
|
|
95
|
+
- lib/generators/testimonials/install/templates/create_testimonials_tables.rb.tt
|
|
96
|
+
- lib/generators/testimonials/install/templates/initializer.rb
|
|
97
|
+
- lib/testimonials.rb
|
|
98
|
+
- lib/testimonials/configuration.rb
|
|
99
|
+
- lib/testimonials/dashboard.js
|
|
100
|
+
- lib/testimonials/engine.rb
|
|
101
|
+
- lib/testimonials/prompt_helper.rb
|
|
102
|
+
- lib/testimonials/version.rb
|
|
103
|
+
- lib/testimonials/widget.js
|
|
104
|
+
- lib/testimonials/widget.rb
|
|
105
|
+
homepage: https://github.com/yshmarov/testimonials-engine
|
|
106
|
+
licenses:
|
|
107
|
+
- MIT
|
|
108
|
+
metadata:
|
|
109
|
+
homepage_uri: https://github.com/yshmarov/testimonials-engine
|
|
110
|
+
source_code_uri: https://github.com/yshmarov/testimonials-engine
|
|
111
|
+
changelog_uri: https://github.com/yshmarov/testimonials-engine/blob/main/CHANGELOG.md
|
|
112
|
+
bug_tracker_uri: https://github.com/yshmarov/testimonials-engine/issues
|
|
113
|
+
rubygems_mfa_required: 'true'
|
|
114
|
+
rdoc_options: []
|
|
115
|
+
require_paths:
|
|
116
|
+
- lib
|
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - ">="
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '3.2'
|
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
|
+
requirements:
|
|
124
|
+
- - ">="
|
|
125
|
+
- !ruby/object:Gem::Version
|
|
126
|
+
version: '0'
|
|
127
|
+
requirements: []
|
|
128
|
+
rubygems_version: 4.0.6
|
|
129
|
+
specification_version: 4
|
|
130
|
+
summary: 'Testimonials, reviews and NPS for Rails: in-app collection widget, public
|
|
131
|
+
collection page, triage dashboard, and a read API.'
|
|
132
|
+
test_files: []
|