qube_rails 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 +7 -0
- data/CHANGELOG.md +161 -0
- data/LICENSE.txt +21 -0
- data/README.md +967 -0
- data/app/assets/javascripts/qube_rails/question.js +457 -0
- data/app/assets/stylesheets/qube_rails.css +1115 -0
- data/app/controllers/concerns/qube_rails/run_authorization.rb +37 -0
- data/app/controllers/qube_rails/connections_controller.rb +141 -0
- data/app/controllers/qube_rails/questions_controller.rb +132 -0
- data/app/controllers/qube_rails/runs_controller.rb +54 -0
- data/app/controllers/qube_rails/storyboard_controller.rb +50 -0
- data/app/controllers/qube_rails/webhooks_controller.rb +99 -0
- data/app/jobs/qube_rails/prune_runs_job.rb +18 -0
- data/app/jobs/qube_rails/refresh_job.rb +18 -0
- data/app/jobs/qube_rails/webhook_job.rb +43 -0
- data/app/models/qube_rails/application_record.rb +17 -0
- data/app/models/qube_rails/connection.rb +303 -0
- data/app/models/qube_rails/notification.rb +12 -0
- data/app/models/qube_rails/queued_request.rb +66 -0
- data/app/models/qube_rails/quickbooks_record.rb +36 -0
- data/app/models/qube_rails/request_delivery.rb +12 -0
- data/app/models/qube_rails/run.rb +187 -0
- data/app/models/qube_rails/sync_mark.rb +21 -0
- data/app/views/qube_rails/_error.html.erb +13 -0
- data/app/views/qube_rails/_question.html.erb +21 -0
- data/app/views/qube_rails/_run_status.html.erb +22 -0
- data/app/views/qube_rails/question/_choice.html.erb +20 -0
- data/app/views/qube_rails/question/_details.html.erb +6 -0
- data/app/views/qube_rails/question/_field.html.erb +15 -0
- data/app/views/qube_rails/question/_footer.html.erb +7 -0
- data/app/views/qube_rails/question/_message.html.erb +2 -0
- data/app/views/qube_rails/question/_pick.html.erb +19 -0
- data/app/views/qube_rails/question/_picks.html.erb +6 -0
- data/app/views/qube_rails/question/_question.html.erb +24 -0
- data/app/views/qube_rails/question/fields/_boolean.html.erb +7 -0
- data/app/views/qube_rails/question/fields/_number.html.erb +7 -0
- data/app/views/qube_rails/question/fields/_rows.html.erb +23 -0
- data/app/views/qube_rails/question/fields/_search.html.erb +23 -0
- data/app/views/qube_rails/question/fields/_select.html.erb +8 -0
- data/app/views/qube_rails/question/fields/_text.html.erb +7 -0
- data/app/views/qube_rails/question/rows/_cell.html.erb +24 -0
- data/app/views/qube_rails/question/rows/_row.html.erb +22 -0
- data/app/views/qube_rails/question/rows/_segmented.html.erb +14 -0
- data/app/views/qube_rails/run_status/_detail.html.erb +7 -0
- data/app/views/qube_rails/run_status/_redeliver.html.erb +5 -0
- data/app/views/qube_rails/run_status/_retry.html.erb +4 -0
- data/app/views/qube_rails/run_status/_stall.html.erb +3 -0
- data/app/views/qube_rails/run_status/_status.html.erb +21 -0
- data/app/views/qube_rails/run_status/_summary.html.erb +2 -0
- data/app/views/qube_rails/storyboard/show.html.erb +86 -0
- data/config/locales/en.yml +46 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20261025000000_create_qube_tables.rb +83 -0
- data/db/migrate/20261026230000_add_qube_sync_status_indexes.rb +13 -0
- data/db/migrate/20261027000000_create_qube_sync_marks.rb +17 -0
- data/db/migrate/20261028000000_create_qube_requests.rb +38 -0
- data/lib/generators/qube_rails/install_generator.rb +47 -0
- data/lib/generators/qube_rails/templates/connection_events.rb.tt +38 -0
- data/lib/generators/qube_rails/templates/initializer.rb.tt +82 -0
- data/lib/generators/qube_rails/workflows_generator.rb +77 -0
- data/lib/qube_rails/backend.rb +70 -0
- data/lib/qube_rails/cache_dedupe.rb +34 -0
- data/lib/qube_rails/configuration.rb +319 -0
- data/lib/qube_rails/connection_events.rb +167 -0
- data/lib/qube_rails/database_dedupe.rb +28 -0
- data/lib/qube_rails/engine.rb +33 -0
- data/lib/qube_rails/json_coder.rb +43 -0
- data/lib/qube_rails/operation.rb +464 -0
- data/lib/qube_rails/question_view.rb +219 -0
- data/lib/qube_rails/request.rb +289 -0
- data/lib/qube_rails/rows_view.rb +146 -0
- data/lib/qube_rails/run_status_view.rb +71 -0
- data/lib/qube_rails/storyboard/ask_about_add.json +60 -0
- data/lib/qube_rails/storyboard/choose.json +51 -0
- data/lib/qube_rails/storyboard/enum_select.json +56 -0
- data/lib/qube_rails/storyboard/missing_record.json +141 -0
- data/lib/qube_rails/storyboard/name_clash.json +59 -0
- data/lib/qube_rails/storyboard/near_match.json +77 -0
- data/lib/qube_rails/storyboard/number_boolean.json +54 -0
- data/lib/qube_rails/storyboard/record_search.json +90 -0
- data/lib/qube_rails/storyboard/refusal.json +53 -0
- data/lib/qube_rails/storyboard/rows.json +359 -0
- data/lib/qube_rails/storyboard.rb +165 -0
- data/lib/qube_rails/sync_status.rb +124 -0
- data/lib/qube_rails/version.rb +5 -0
- data/lib/qube_rails/view_helpers.rb +29 -0
- data/lib/qube_rails.rb +201 -0
- metadata +180 -0
data/lib/qube_rails.rb
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
require "qube_rails/version"
|
|
5
|
+
require "qube_rails/backend"
|
|
6
|
+
require "qube_rails/configuration"
|
|
7
|
+
require "qube_rails/cache_dedupe"
|
|
8
|
+
require "qube_rails/database_dedupe"
|
|
9
|
+
require "qube_rails/json_coder"
|
|
10
|
+
require "qube_rails/operation"
|
|
11
|
+
require "qube_rails/request"
|
|
12
|
+
require "qube_rails/sync_status"
|
|
13
|
+
require "qube_rails/connection_events"
|
|
14
|
+
require "qube_rails/question_view"
|
|
15
|
+
require "qube_rails/run_status_view"
|
|
16
|
+
require "qube_rails/view_helpers"
|
|
17
|
+
require "qube_rails/storyboard"
|
|
18
|
+
|
|
19
|
+
# QuBe Sync's Rails engine: the code that's the same in every Rails app's integration. Your app
|
|
20
|
+
# keeps its own: config/initializers/qube_rails.rb (QubeRails.configure), an operation class per
|
|
21
|
+
# way it starts a workflow (QubeRails::Operation), its class for a connection's events
|
|
22
|
+
# (QubeRails::ConnectionEvents), and `mount QubeRails::Engine => "/"` in config/routes.rb. The
|
|
23
|
+
# engine's own tables (qube_connections, qube_runs, qube_quickbooks_records) keep every
|
|
24
|
+
# connection, every run and every QuickBooks id, so your tables need no QuBe columns.
|
|
25
|
+
module QubeRails
|
|
26
|
+
# A run whose operation none of config.operations has any more (taken out after it started).
|
|
27
|
+
class UnknownOperation < StandardError; end
|
|
28
|
+
|
|
29
|
+
# A connection's event came before the row's qube_id was saved (create_connection hadn't
|
|
30
|
+
# returned yet, or its update hadn't committed: a simulated connection sends
|
|
31
|
+
# connection.connected at once). The job raises it so the queue retries once it's there.
|
|
32
|
+
class NotReady < StandardError; end
|
|
33
|
+
|
|
34
|
+
# Operation.start was given a connection QuBe Sync hasn't made yet (no qube_id): the customer
|
|
35
|
+
# hasn't finished onboarding, or QuBe Sync didn't answer it.
|
|
36
|
+
class NotConnected < ArgumentError; end
|
|
37
|
+
|
|
38
|
+
# An operation's input has no value for a reference QuickBooks requires (an invoice's
|
|
39
|
+
# customer_ref): the record has no QuickBooks link on the run's connection and no name to look
|
|
40
|
+
# it up by. The kit's mapped operations raise it from input, so the start fails, before anything
|
|
41
|
+
# is sent, saying which reference and which record.
|
|
42
|
+
class MissingReference < ArgumentError; end
|
|
43
|
+
|
|
44
|
+
# QuBe Sync refused a start (422 realtime_required): workflows run on realtime connections, and
|
|
45
|
+
# this one is standard. Its message says how to upgrade it.
|
|
46
|
+
class RealtimeRequired < StandardError; end
|
|
47
|
+
|
|
48
|
+
# Seconds after an event before a record's pages are told to refresh once more.
|
|
49
|
+
REFRESH_AGAIN = 5
|
|
50
|
+
|
|
51
|
+
class << self
|
|
52
|
+
def config
|
|
53
|
+
@config ||= Configuration.new
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# In config/initializers/qube_rails.rb:
|
|
57
|
+
# QubeRails.configure do |config|
|
|
58
|
+
# config.operations = { "create_invoice" => "QuickbooksDesktop::CreateInvoice" }
|
|
59
|
+
# config.connection_events = "QuickbooksDesktop::ConnectionEvents"
|
|
60
|
+
# ...
|
|
61
|
+
# end
|
|
62
|
+
def configure
|
|
63
|
+
yield config
|
|
64
|
+
@client = nil
|
|
65
|
+
config
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# QuBe Sync's API, through the qube_sync gem (QubeSync::Client): create_connection,
|
|
69
|
+
# start_run, answer, search, get_run, cancel_run, queue and the rest. A non-2xx answer raises
|
|
70
|
+
# QubeSync::APIError (status, code, message, body).
|
|
71
|
+
def client
|
|
72
|
+
@client ||= Backend.client(api_key: config.api_key, base_url: config.base_url,
|
|
73
|
+
on_retry: method(:log_retry))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# A call QuBe Sync didn't answer, sent again (safe calls only, within 60 seconds).
|
|
77
|
+
def log_retry(info)
|
|
78
|
+
why = info[:status] || (info[:error] && info[:error].class)
|
|
79
|
+
logger.warn("QuBe Sync: retrying #{info[:method]} #{info[:url]} (#{why}) " \
|
|
80
|
+
"in #{info[:delay].to_f.round(1)} s")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Runs one verified delivery. kind is "run" (QUBE_WEBHOOK_URL/runs/<id>): the run's
|
|
84
|
+
# operation finds its record and handles the event (QubeRails::Operation#handle); or
|
|
85
|
+
# "connection" (QUBE_WEBHOOK_URL/connections/<id>): your config.connection_events class
|
|
86
|
+
# handles it for that connection; or "request" (QUBE_WEBHOOK_URL/requests/<id>): the request's
|
|
87
|
+
# class handles it (QubeRails::Request#handle). QubeRails::WebhookJob calls it; so does your own queue's
|
|
88
|
+
# worker when config.enqueue hands deliveries elsewhere.
|
|
89
|
+
#
|
|
90
|
+
# body is the delivery's raw body, which the webhook endpoint verified: parsed into its typed
|
|
91
|
+
# event here, and not verified again, since a job can run after its timestamp's 5 minutes are
|
|
92
|
+
# up.
|
|
93
|
+
def run(kind, id, body)
|
|
94
|
+
event = Backend.parse(body)
|
|
95
|
+
case kind.to_s
|
|
96
|
+
when "run" then run_event(id, event)
|
|
97
|
+
when "connection" then connection_event(id, event)
|
|
98
|
+
when "request" then request_event(id, event)
|
|
99
|
+
else raise ArgumentError, "QuBe Sync: a delivery is for a run, a connection or a request, not #{kind.inspect}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Tells the pages showing record's runs to refresh: the engine's partials subscribe with
|
|
104
|
+
# turbo_stream_from(record, :qube_rails). Only in an app with turbo-rails 2 (Turbo 8 page
|
|
105
|
+
# refreshes) and Action Cable; otherwise nothing. Once now, and once more REFRESH_AGAIN
|
|
106
|
+
# later (QubeRails::RefreshJob), for a page that was still loading the first time. A
|
|
107
|
+
# broadcast that fails is logged: the event is applied either way, and a reload shows it.
|
|
108
|
+
def broadcast_refresh(record)
|
|
109
|
+
return unless refresh_broadcasts?
|
|
110
|
+
|
|
111
|
+
broadcast_refresh_now(record)
|
|
112
|
+
QubeRails::RefreshJob.set(wait: REFRESH_AGAIN).perform_later(record)
|
|
113
|
+
rescue StandardError => e
|
|
114
|
+
logger.warn("QuBe Sync: couldn't tell the page for #{record.class.name} #{record.id} to refresh again " \
|
|
115
|
+
"(#{e.class})")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def broadcast_refresh_now(record)
|
|
119
|
+
return unless refresh_broadcasts?
|
|
120
|
+
|
|
121
|
+
Turbo::StreamsChannel.broadcast_refresh_to(record, :qube_rails)
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
logger.warn("QuBe Sync: couldn't tell the page for #{record.class.name} #{record.id} to refresh " \
|
|
124
|
+
"(#{e.class})")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def refresh_broadcasts?
|
|
128
|
+
defined?(Turbo::StreamsChannel) && Turbo::StreamsChannel.respond_to?(:broadcast_refresh_to)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def logger
|
|
132
|
+
Rails.logger
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# What the screens say to a person, from I18n under qube_rails.* (config/locales/en.yml has
|
|
136
|
+
# every key): an app overrides or translates a key in its own locale files. A locale that
|
|
137
|
+
# lacks a key falls back to the engine's English.
|
|
138
|
+
def t(key, **options)
|
|
139
|
+
I18n.t(key, **options, scope: :qube_rails, raise: true)
|
|
140
|
+
rescue I18n::MissingTranslationData
|
|
141
|
+
I18n.t(key, **options, scope: :qube_rails, locale: :en)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# A run, connection or record that isn't there is dropped with a warning, not raised: the
|
|
145
|
+
# endpoint answered 404 unless the row existed, and the app saves a record before its run's
|
|
146
|
+
# row, so by the time a job runs, a missing one was deleted, and retrying would never
|
|
147
|
+
# succeed. An operation taken out of config.operations raises (UnknownOperation): putting it
|
|
148
|
+
# back and retrying does.
|
|
149
|
+
def run_event(id, event)
|
|
150
|
+
run = Run.find_by(id: id)
|
|
151
|
+
return dropped("run #{id} isn't there", event) unless run
|
|
152
|
+
|
|
153
|
+
operation = config.operation_class(run.operation)
|
|
154
|
+
raise UnknownOperation, "QuBe Sync: no operation has the slug #{run.operation.inspect}" unless operation
|
|
155
|
+
|
|
156
|
+
record = find_run_record(operation, run)
|
|
157
|
+
return dropped("run #{run.id}'s #{run.record_type} #{run.record_id} isn't there", event) unless record
|
|
158
|
+
|
|
159
|
+
operation.new(record, run).handle(event)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def request_event(id, event)
|
|
163
|
+
row = QueuedRequest.find_by(id: id)
|
|
164
|
+
return dropped("request #{id} isn't there", event) unless row
|
|
165
|
+
|
|
166
|
+
request_class = config.operation_class(row.operation)
|
|
167
|
+
raise UnknownOperation, "QuBe Sync: no operation has the slug #{row.operation.inspect}" unless request_class
|
|
168
|
+
|
|
169
|
+
record = find_run_record(request_class, row)
|
|
170
|
+
return dropped("request #{row.id}'s #{row.record_type} #{row.record_id} isn't there", event) unless record
|
|
171
|
+
|
|
172
|
+
request_class.new(record, row).handle(event)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def connection_event(id, event)
|
|
176
|
+
connection = Connection.find_by(id: id)
|
|
177
|
+
return dropped("connection #{id} isn't there", event) unless connection
|
|
178
|
+
|
|
179
|
+
config.connection_events_class.new(connection).handle(event)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def find_run_record(operation, run)
|
|
183
|
+
operation.record_for(run)
|
|
184
|
+
rescue ActiveRecord::RecordNotFound # a find_record written with find
|
|
185
|
+
nil
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def dropped(why, event)
|
|
189
|
+
logger.warn("QuBe Sync: dropped #{event.event || event.type}: #{why} (deleted since, so it can't be applied)")
|
|
190
|
+
nil
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Forgets the settings and the client: for tests.
|
|
194
|
+
def reset!
|
|
195
|
+
@config = nil
|
|
196
|
+
@client = nil
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
require "qube_rails/engine" if defined?(Rails::Engine)
|
metadata
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: qube_rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Garrett Lancaster
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-10-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: qube_sync
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.5.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.5.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: railties
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '7.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '7.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: activejob
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '7.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '7.0'
|
|
55
|
+
description: 'The code every Rails app''s QuBe Sync integration shares: the webhook
|
|
56
|
+
endpoint and its job, onboarding, the question routes and partial, and the Operation
|
|
57
|
+
and ConnectionEvents bases. Your app keeps its own operations, its connection class
|
|
58
|
+
and one initializer.'
|
|
59
|
+
email:
|
|
60
|
+
- support@qubesync.com
|
|
61
|
+
executables: []
|
|
62
|
+
extensions: []
|
|
63
|
+
extra_rdoc_files: []
|
|
64
|
+
files:
|
|
65
|
+
- CHANGELOG.md
|
|
66
|
+
- LICENSE.txt
|
|
67
|
+
- README.md
|
|
68
|
+
- app/assets/javascripts/qube_rails/question.js
|
|
69
|
+
- app/assets/stylesheets/qube_rails.css
|
|
70
|
+
- app/controllers/concerns/qube_rails/run_authorization.rb
|
|
71
|
+
- app/controllers/qube_rails/connections_controller.rb
|
|
72
|
+
- app/controllers/qube_rails/questions_controller.rb
|
|
73
|
+
- app/controllers/qube_rails/runs_controller.rb
|
|
74
|
+
- app/controllers/qube_rails/storyboard_controller.rb
|
|
75
|
+
- app/controllers/qube_rails/webhooks_controller.rb
|
|
76
|
+
- app/jobs/qube_rails/prune_runs_job.rb
|
|
77
|
+
- app/jobs/qube_rails/refresh_job.rb
|
|
78
|
+
- app/jobs/qube_rails/webhook_job.rb
|
|
79
|
+
- app/models/qube_rails/application_record.rb
|
|
80
|
+
- app/models/qube_rails/connection.rb
|
|
81
|
+
- app/models/qube_rails/notification.rb
|
|
82
|
+
- app/models/qube_rails/queued_request.rb
|
|
83
|
+
- app/models/qube_rails/quickbooks_record.rb
|
|
84
|
+
- app/models/qube_rails/request_delivery.rb
|
|
85
|
+
- app/models/qube_rails/run.rb
|
|
86
|
+
- app/models/qube_rails/sync_mark.rb
|
|
87
|
+
- app/views/qube_rails/_error.html.erb
|
|
88
|
+
- app/views/qube_rails/_question.html.erb
|
|
89
|
+
- app/views/qube_rails/_run_status.html.erb
|
|
90
|
+
- app/views/qube_rails/question/_choice.html.erb
|
|
91
|
+
- app/views/qube_rails/question/_details.html.erb
|
|
92
|
+
- app/views/qube_rails/question/_field.html.erb
|
|
93
|
+
- app/views/qube_rails/question/_footer.html.erb
|
|
94
|
+
- app/views/qube_rails/question/_message.html.erb
|
|
95
|
+
- app/views/qube_rails/question/_pick.html.erb
|
|
96
|
+
- app/views/qube_rails/question/_picks.html.erb
|
|
97
|
+
- app/views/qube_rails/question/_question.html.erb
|
|
98
|
+
- app/views/qube_rails/question/fields/_boolean.html.erb
|
|
99
|
+
- app/views/qube_rails/question/fields/_number.html.erb
|
|
100
|
+
- app/views/qube_rails/question/fields/_rows.html.erb
|
|
101
|
+
- app/views/qube_rails/question/fields/_search.html.erb
|
|
102
|
+
- app/views/qube_rails/question/fields/_select.html.erb
|
|
103
|
+
- app/views/qube_rails/question/fields/_text.html.erb
|
|
104
|
+
- app/views/qube_rails/question/rows/_cell.html.erb
|
|
105
|
+
- app/views/qube_rails/question/rows/_row.html.erb
|
|
106
|
+
- app/views/qube_rails/question/rows/_segmented.html.erb
|
|
107
|
+
- app/views/qube_rails/run_status/_detail.html.erb
|
|
108
|
+
- app/views/qube_rails/run_status/_redeliver.html.erb
|
|
109
|
+
- app/views/qube_rails/run_status/_retry.html.erb
|
|
110
|
+
- app/views/qube_rails/run_status/_stall.html.erb
|
|
111
|
+
- app/views/qube_rails/run_status/_status.html.erb
|
|
112
|
+
- app/views/qube_rails/run_status/_summary.html.erb
|
|
113
|
+
- app/views/qube_rails/storyboard/show.html.erb
|
|
114
|
+
- config/locales/en.yml
|
|
115
|
+
- config/routes.rb
|
|
116
|
+
- db/migrate/20261025000000_create_qube_tables.rb
|
|
117
|
+
- db/migrate/20261026230000_add_qube_sync_status_indexes.rb
|
|
118
|
+
- db/migrate/20261027000000_create_qube_sync_marks.rb
|
|
119
|
+
- db/migrate/20261028000000_create_qube_requests.rb
|
|
120
|
+
- lib/generators/qube_rails/install_generator.rb
|
|
121
|
+
- lib/generators/qube_rails/templates/connection_events.rb.tt
|
|
122
|
+
- lib/generators/qube_rails/templates/initializer.rb.tt
|
|
123
|
+
- lib/generators/qube_rails/workflows_generator.rb
|
|
124
|
+
- lib/qube_rails.rb
|
|
125
|
+
- lib/qube_rails/backend.rb
|
|
126
|
+
- lib/qube_rails/cache_dedupe.rb
|
|
127
|
+
- lib/qube_rails/configuration.rb
|
|
128
|
+
- lib/qube_rails/connection_events.rb
|
|
129
|
+
- lib/qube_rails/database_dedupe.rb
|
|
130
|
+
- lib/qube_rails/engine.rb
|
|
131
|
+
- lib/qube_rails/json_coder.rb
|
|
132
|
+
- lib/qube_rails/operation.rb
|
|
133
|
+
- lib/qube_rails/question_view.rb
|
|
134
|
+
- lib/qube_rails/request.rb
|
|
135
|
+
- lib/qube_rails/rows_view.rb
|
|
136
|
+
- lib/qube_rails/run_status_view.rb
|
|
137
|
+
- lib/qube_rails/storyboard.rb
|
|
138
|
+
- lib/qube_rails/storyboard/ask_about_add.json
|
|
139
|
+
- lib/qube_rails/storyboard/choose.json
|
|
140
|
+
- lib/qube_rails/storyboard/enum_select.json
|
|
141
|
+
- lib/qube_rails/storyboard/missing_record.json
|
|
142
|
+
- lib/qube_rails/storyboard/name_clash.json
|
|
143
|
+
- lib/qube_rails/storyboard/near_match.json
|
|
144
|
+
- lib/qube_rails/storyboard/number_boolean.json
|
|
145
|
+
- lib/qube_rails/storyboard/record_search.json
|
|
146
|
+
- lib/qube_rails/storyboard/refusal.json
|
|
147
|
+
- lib/qube_rails/storyboard/rows.json
|
|
148
|
+
- lib/qube_rails/sync_status.rb
|
|
149
|
+
- lib/qube_rails/version.rb
|
|
150
|
+
- lib/qube_rails/view_helpers.rb
|
|
151
|
+
homepage: https://qubesync.com
|
|
152
|
+
licenses:
|
|
153
|
+
- MIT
|
|
154
|
+
metadata:
|
|
155
|
+
homepage_uri: https://qubesync.com
|
|
156
|
+
documentation_uri: https://docs.qubesync.com
|
|
157
|
+
source_code_uri: https://github.com/qubeintegrations/qube_rails
|
|
158
|
+
changelog_uri: https://github.com/qubeintegrations/qube_rails/blob/main/CHANGELOG.md
|
|
159
|
+
rubygems_mfa_required: 'true'
|
|
160
|
+
post_install_message:
|
|
161
|
+
rdoc_options: []
|
|
162
|
+
require_paths:
|
|
163
|
+
- lib
|
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
|
+
requirements:
|
|
166
|
+
- - ">="
|
|
167
|
+
- !ruby/object:Gem::Version
|
|
168
|
+
version: 3.3.0
|
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
requirements: []
|
|
175
|
+
rubygems_version: 3.5.22
|
|
176
|
+
signing_key:
|
|
177
|
+
specification_version: 4
|
|
178
|
+
summary: 'QuBe Sync''s Rails engine: the webhook endpoint, onboarding and question
|
|
179
|
+
routes for QuickBooks Desktop'
|
|
180
|
+
test_files: []
|