bullet_train 1.0.26 → 1.0.29
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/models/concerns/current_attributes/base.rb +36 -0
- data/app/views/bullet_train/partial_resolver.html.erb +2 -0
- data/app/views/devise/sessions/new.html.erb +0 -2
- data/app/views/layouts/docs.html.erb +305 -0
- data/app/views/layouts/mailer.html.erb +65 -0
- data/app/views/public/home/docs.html.erb +1 -0
- data/app/views/user_mailer/invited.html.erb +29 -0
- data/app/views/user_mailer/welcome.html.erb +31 -0
- data/config/locales/en/base.yml +132 -0
- data/config/locales/en/doorkeeper.en.yml +152 -0
- data/config/locales/en/oauth.en.yml +15 -0
- data/config/locales/en/roles.en.yml +6 -0
- data/config/locales/en/user_mailer.en.yml +36 -0
- data/lib/bullet_train/resolver.rb +148 -0
- data/lib/bullet_train/version.rb +1 -1
- data/lib/bullet_train.rb +1 -0
- data/lib/tasks/bullet_train_tasks.rake +31 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b21c5d19664f00d67f8d24604a6aadf16d34074b4471118893dc4245d52aa048
|
4
|
+
data.tar.gz: 68866b87e82a35d9005c0e75259f44f50a0b2607176e78d7de4f7d6f729a82be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8744327543da313a00acb719c0b838fb380c5ba10946291ecb3c7d0ba83c927a3cb98ecff024a93480ad0ed044bc1e171c91c8def9c4c58679ce2be5db4e6b51
|
7
|
+
data.tar.gz: 74d3acfc5ab9258eb3a3eaaea9e313757a26469404d04bfc5b0c0f210c2f60d3a39abe81d8d563d23a55d019ed813f55a583683d7d2ee24b23379a870cec23c6
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CurrentAttributes::Base
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
attribute :user, :team, :membership, :ability
|
6
|
+
|
7
|
+
resets do
|
8
|
+
Time.zone = nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def user=(user)
|
13
|
+
super
|
14
|
+
|
15
|
+
if user
|
16
|
+
Time.zone = user.time_zone
|
17
|
+
self.ability = Ability.new(user)
|
18
|
+
else
|
19
|
+
Time.zone = nil
|
20
|
+
self.ability = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
update_membership
|
24
|
+
end
|
25
|
+
|
26
|
+
def team=(team)
|
27
|
+
super
|
28
|
+
update_membership
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_membership
|
32
|
+
self.membership = if user && team
|
33
|
+
user.memberships.where(team: team)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
|
3
|
+
<html>
|
4
|
+
<head>
|
5
|
+
<%
|
6
|
+
# we're going to use the
|
7
|
+
body = Nokogiri::HTML(@body)
|
8
|
+
title = body.css('h1').first.text.presence || t('bullet_train.tagline')
|
9
|
+
first_paragraph = body.css('p').first
|
10
|
+
preceding_heading = first_paragraph&.xpath("preceding-sibling::h2")
|
11
|
+
description = [preceding_heading&.text, first_paragraph&.text].select(&:present?).join(" — ") || t('bullet_train.description')
|
12
|
+
image_url = "https://avatars.githubusercontent.com/u/5976880?s=280&v=4"
|
13
|
+
site_name = "Bullet Train Developer Documentation"
|
14
|
+
%>
|
15
|
+
|
16
|
+
<% content_for :title do %>
|
17
|
+
<title><%= [site_name, title, t('bullet_train.tagline')].select(&:present?).uniq.first(2).reverse.join(' — ') %></title>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<%= render 'themes/light/layouts/head' %>
|
21
|
+
<meta content="<%= t('bullet_train.keywords') %>" name="keywords" />
|
22
|
+
<meta content="<%= description.truncate(200) %>" name="description" />
|
23
|
+
<meta name="twitter:image:src" content="<%= image_url %>" />
|
24
|
+
<meta name="twitter:site" content="@bullettrainco" />
|
25
|
+
<meta name="twitter:card" content="summary" />
|
26
|
+
<meta name="twitter:title" content="<%= title.truncate(70) %>" />
|
27
|
+
<meta name="twitter:description" content="<%= description.truncate(160) %>" />
|
28
|
+
<meta property="og:image" content="<%= image_url %>" />
|
29
|
+
<meta property="og:site_name" content="<%= site_name %>" />
|
30
|
+
<meta property="og:type" content="object" />
|
31
|
+
<meta property="og:title" content="<%= title.truncate(95) %>" />
|
32
|
+
<meta property="og:url" content="<%= request.base_url + request.path %>" />
|
33
|
+
<meta property="og:description" content="<%= description.truncate(200) %>" />
|
34
|
+
</head>
|
35
|
+
<body class="bg-light-blue-gradient text-gray-700 text-sm font-normal dark:bg-dark-blue-gradient dark:text-sealBlue-900">
|
36
|
+
<div class="md:p-5">
|
37
|
+
<div class="h-screen md:h-auto overflow-hidden md:rounded-lg flex shadow"
|
38
|
+
data-controller="mobile-menu"
|
39
|
+
data-mobile-menu-hidden-class="hidden"
|
40
|
+
data-mobile-menu-show-event-name-value="mobile-menu:show"
|
41
|
+
data-mobile-menu-hide-event-name-value="mobile-menu:hide"
|
42
|
+
>
|
43
|
+
|
44
|
+
<% menu = capture do %>
|
45
|
+
<div class="flex items-center flex-shrink-0 p-4 bg-blue-darker md:rounded-tl-lg">
|
46
|
+
<%= image_tag image_path("light/logo/logo.png"), class: 'h-5 w-auto mx-auto' %>
|
47
|
+
|
48
|
+
<div class="lg:hidden absolute right-0">
|
49
|
+
<button class="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
|
50
|
+
data-action="reveal#hide"
|
51
|
+
>
|
52
|
+
<span class="sr-only">Close Application Menu</span>
|
53
|
+
<svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
54
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
55
|
+
</svg>
|
56
|
+
</button>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<nav class="flex-1 space-y-1 overflow-y-auto">
|
61
|
+
<div class="px-5 py-4" id="menu">
|
62
|
+
|
63
|
+
<%= render 'account/shared/menu/section', title: 'Introduction' do %>
|
64
|
+
<%= render 'account/shared/menu/item', url: '/docs', label: 'Table of Contents' do |p| %>
|
65
|
+
<% p.content_for :icon do %>
|
66
|
+
<i class="fal fa-home-lg-alt ti ti-list"></i>
|
67
|
+
<% end %>
|
68
|
+
<% end %>
|
69
|
+
|
70
|
+
<%= render 'account/shared/menu/item', url: '/docs/getting-started', label: 'Getting Started' do |p| %>
|
71
|
+
<% p.content_for :icon do %>
|
72
|
+
<i class="fal fa-terminal ti ti-flag"></i>
|
73
|
+
<% end %>
|
74
|
+
<% end %>
|
75
|
+
|
76
|
+
<%= render 'account/shared/menu/item', url: '/docs/philosophy', label: 'Philosophy' do |p| %>
|
77
|
+
<% p.content_for :icon do %>
|
78
|
+
<i class="fal fa-lightbulb-on ti ti-light-bulb"></i>
|
79
|
+
<% end %>
|
80
|
+
<% end %>
|
81
|
+
|
82
|
+
<%= render 'account/shared/menu/item', url: '/docs/upgrades', label: 'Upgrades' do |p| %>
|
83
|
+
<% p.content_for :icon do %>
|
84
|
+
<i class="fal fa-sparkles ti ti-arrow-up"></i>
|
85
|
+
<% end %>
|
86
|
+
<% end %>
|
87
|
+
|
88
|
+
<%= render 'account/shared/menu/item', url: '/docs/tunneling', label: 'Tunneling' do |p| %>
|
89
|
+
<% p.content_for :icon do %>
|
90
|
+
<i class="fal fa-bolt ti ti-bolt"></i>
|
91
|
+
<% end %>
|
92
|
+
<% end %>
|
93
|
+
<% end %>
|
94
|
+
|
95
|
+
<%= render 'account/shared/menu/section', title: 'Developer Tools' do %>
|
96
|
+
<%= render 'account/shared/menu/item', url: '/docs/super-scaffolding', label: 'Super Scaffolding' do |p| %>
|
97
|
+
<% p.content_for :icon do %>
|
98
|
+
<i class="fal fa-magic ti ti-wand"></i>
|
99
|
+
<% end %>
|
100
|
+
<% end %>
|
101
|
+
|
102
|
+
<%= render 'account/shared/menu/item', url: '/docs/field-partials', label: 'Field Partials' do |p| %>
|
103
|
+
<% p.content_for :icon do %>
|
104
|
+
<i class="fal fa-i-cursor ti ti-text"></i>
|
105
|
+
<% end %>
|
106
|
+
<% end %>
|
107
|
+
|
108
|
+
<%= render 'account/shared/menu/item', url: '/docs/themes', label: 'Themes' do |p| %>
|
109
|
+
<% p.content_for :icon do %>
|
110
|
+
<i class="fal fa-swatchbook ti ti-palette"></i>
|
111
|
+
<% end %>
|
112
|
+
<% end %>
|
113
|
+
|
114
|
+
<%= render 'account/shared/menu/item', url: '/docs/seeds', label: 'Database Seeds' do |p| %>
|
115
|
+
<% p.content_for :icon do %>
|
116
|
+
<i class="fal fa-seedling ti ti-server"></i>
|
117
|
+
<% end %>
|
118
|
+
<% end %>
|
119
|
+
|
120
|
+
<%= render 'account/shared/menu/item', url: '/docs/testing', label: 'Test Suite' do |p| %>
|
121
|
+
<% p.content_for :icon do %>
|
122
|
+
<i class="fal fa-check ti ti-check"></i>
|
123
|
+
<% end %>
|
124
|
+
<% end %>
|
125
|
+
<% end %>
|
126
|
+
|
127
|
+
<%= render 'account/shared/menu/section', title: 'Accounts & Teams' do %>
|
128
|
+
<%= render 'account/shared/menu/item', url: '/docs/authentication', label: 'Authentication' do |p| %>
|
129
|
+
<% p.content_for :icon do %>
|
130
|
+
<i class="fal fa-fingerprint ti ti-id-badge"></i>
|
131
|
+
<% end %>
|
132
|
+
<% end %>
|
133
|
+
|
134
|
+
<%= render 'account/shared/menu/item', url: '/docs/teams', label: 'Teams' do |p| %>
|
135
|
+
<% p.content_for :icon do %>
|
136
|
+
<i class="fal fa-users ti ti-user"></i>
|
137
|
+
<% end %>
|
138
|
+
<% end %>
|
139
|
+
|
140
|
+
<%= render 'account/shared/menu/item', url: '/docs/permissions', label: 'Roles & Permissions' do |p| %>
|
141
|
+
<% p.content_for :icon do %>
|
142
|
+
<i class="fal fa-lock-alt ti ti-lock"></i>
|
143
|
+
<% end %>
|
144
|
+
<% end %>
|
145
|
+
|
146
|
+
<%= render 'account/shared/menu/item', url: '/docs/onboarding', label: 'Onboarding' do |p| %>
|
147
|
+
<% p.content_for :icon do %>
|
148
|
+
<i class="fal fa-snowboarding ti ti-direction"></i>
|
149
|
+
<% end %>
|
150
|
+
<% end %>
|
151
|
+
|
152
|
+
<%= render 'account/shared/menu/item', url: '/docs/namespacing', label: 'Namespacing' do |p| %>
|
153
|
+
<% p.content_for :icon do %>
|
154
|
+
<i class="fal fa-object-group ti ti-widgetized"></i>
|
155
|
+
<% end %>
|
156
|
+
<% end %>
|
157
|
+
<% end %>
|
158
|
+
|
159
|
+
<%= render 'account/shared/menu/section', title: 'Billing' do %>
|
160
|
+
<%= render 'account/shared/menu/item', url: '/docs/billing/stripe', label: 'Stripe' do |p| %>
|
161
|
+
<% p.content_for :icon do %>
|
162
|
+
<i class="fab fa-stripe-s ti ti-money"></i>
|
163
|
+
<% end %>
|
164
|
+
<% end %>
|
165
|
+
<% end %>
|
166
|
+
|
167
|
+
<%= render 'account/shared/menu/section', title: 'Integration' do %>
|
168
|
+
<%= render 'account/shared/menu/item', url: '/docs/oauth', label: 'OAuth Providers' do |p| %>
|
169
|
+
<% p.content_for :icon do %>
|
170
|
+
<i class="fal fa-at ti ti-reload"></i>
|
171
|
+
<% end %>
|
172
|
+
<% end %>
|
173
|
+
|
174
|
+
<%= render 'account/shared/menu/item', url: '/docs/api', label: 'REST API' do |p| %>
|
175
|
+
<% p.content_for :icon do %>
|
176
|
+
<i class="fal fa-brackets-curly ti ti-settings"></i>
|
177
|
+
<% end %>
|
178
|
+
<% end if false %>
|
179
|
+
|
180
|
+
<%= render 'account/shared/menu/item', url: '/docs/webhooks/outgoing', label: 'Outgoing Webhooks' do |p| %>
|
181
|
+
<% p.content_for :icon do %>
|
182
|
+
<i class="fal fa-outlet ti ti-pulse"></i>
|
183
|
+
<% end %>
|
184
|
+
<% end if false %>
|
185
|
+
|
186
|
+
<%= render 'account/shared/menu/item', url: '/docs/webhooks/incoming', label: 'Incoming Webhooks' do |p| %>
|
187
|
+
<% p.content_for :icon do %>
|
188
|
+
<i class="fal fa-plug ti ti-plug"></i>
|
189
|
+
<% end %>
|
190
|
+
<% end if false %>
|
191
|
+
<% end %>
|
192
|
+
|
193
|
+
<%= render 'account/shared/menu/section', title: 'Internationalization' do %>
|
194
|
+
<%= render 'account/shared/menu/item', url: '/docs/i18n', label: 'Translations' do |p| %>
|
195
|
+
<% p.content_for :icon do %>
|
196
|
+
<i class="fal fa-language ti ti-world"></i>
|
197
|
+
<% end %>
|
198
|
+
<% end %>
|
199
|
+
<% end if false %>
|
200
|
+
|
201
|
+
<%= render 'account/shared/menu/section', title: 'Add-Ons' do %>
|
202
|
+
<%= render 'account/shared/menu/item', url: '/docs/font-awesome-pro', label: 'Font Awesome Pro' do |p| %>
|
203
|
+
<% p.content_for :icon do %>
|
204
|
+
<i class="fal fa-flag ti ti-flag"></i>
|
205
|
+
<% end %>
|
206
|
+
<% end %>
|
207
|
+
<% end %>
|
208
|
+
|
209
|
+
<%= render 'account/shared/menu/section', title: 'Deployment' do %>
|
210
|
+
<%= render 'account/shared/menu/item', url: '/docs/heroku', label: 'Heroku' do |p| %>
|
211
|
+
<% p.content_for :icon do %>
|
212
|
+
<i class="fal fa-cloud ti ti-cloud-up"></i>
|
213
|
+
<% end %>
|
214
|
+
<% end %>
|
215
|
+
|
216
|
+
<%= render 'account/shared/menu/item', url: '/docs/desktop', label: 'Desktop Applications' do |p| %>
|
217
|
+
<% p.content_for :icon do %>
|
218
|
+
<i class="fal fa-window-restore ti ti-desktop"></i>
|
219
|
+
<% end %>
|
220
|
+
<% end %>
|
221
|
+
<% end %>
|
222
|
+
</div>
|
223
|
+
</nav>
|
224
|
+
<% end %>
|
225
|
+
|
226
|
+
<div class="lg:hidden hidden"
|
227
|
+
data-mobile-menu-target="wrapper"
|
228
|
+
|
229
|
+
data-controller="reveal"
|
230
|
+
data-reveal-away-value="true"
|
231
|
+
data-reveal-hide-keys-value="escape"
|
232
|
+
|
233
|
+
data-action="mobile-menu:show->reveal#show mobile-menu:hide->reveal#hide mobile-menu-toggle->reveal#toggle reveal:hidden->mobile-menu#hideWrapper"
|
234
|
+
>
|
235
|
+
<div class="fixed inset-0 flex z-40">
|
236
|
+
<button
|
237
|
+
data-action="reveal#hide"
|
238
|
+
|
239
|
+
hidden
|
240
|
+
data-reveal
|
241
|
+
data-transition
|
242
|
+
data-transition-enter="transition-opacity ease-linear duration-200"
|
243
|
+
data-transition-enter-start="opacity-0"
|
244
|
+
data-transition-enter-end="opacity-100"
|
245
|
+
data-transition-leave="transition-opacity ease-linear duration-200"
|
246
|
+
data-transition-leave-start="opacity-100"
|
247
|
+
data-transition-leave-end="opacity-0"
|
248
|
+
|
249
|
+
class="fixed inset-0" aria-hidden="true"
|
250
|
+
>
|
251
|
+
<div class="absolute inset-0 bg-light-blue-gradient opacity-75"></div>
|
252
|
+
</button>
|
253
|
+
<div
|
254
|
+
hidden
|
255
|
+
data-reveal
|
256
|
+
data-transition
|
257
|
+
data-transition-enter="transition ease-in-out duration-200 transform"
|
258
|
+
data-transition-enter-start="-translate-x-full"
|
259
|
+
data-transition-enter-end="translate-x-0"
|
260
|
+
data-transition-leave="transition ease-in-out duration-200 transform"
|
261
|
+
data-transition-leave-start="translate-x-0"
|
262
|
+
data-transition-leave-end="-translate-x-full"
|
263
|
+
|
264
|
+
class="relative flex-1 flex flex-col max-w-xs w-full shadow-xl bg-gradient-to-b from-vividBlue-700 to-vividBlue-800 dark:from-sealBlue-200 dark:to-sealBlue-200"
|
265
|
+
>
|
266
|
+
<%= menu %>
|
267
|
+
</div>
|
268
|
+
<div class="flex-shrink-0 w-14" aria-hidden="true"></div>
|
269
|
+
</div>
|
270
|
+
</div>
|
271
|
+
|
272
|
+
<div class="hidden lg:flex lg:flex-shrink-0 bg-gradient-to-b from-vividBlue-700 to-vividBlue-800 dark:from-sealBlue-200 dark:to-sealBlue-200">
|
273
|
+
<div class="w-64">
|
274
|
+
<%= menu %>
|
275
|
+
</div>
|
276
|
+
</div>
|
277
|
+
|
278
|
+
<div class="flex flex-col w-0 flex-1 overflow-hidden bg-gray-100 dark:bg-sealBlue-200 lg:border-l dark:border-gray-500">
|
279
|
+
<main class="flex-1 relative z-0 overflow-y-auto focus:outline-none" tabindex="0">
|
280
|
+
|
281
|
+
<button class="lg:hidden h-12 w-12 ml-1 flex-none inline-flex items-center justify-center rounded-md text-gray-500 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue"
|
282
|
+
data-action="mobile-menu#toggle"
|
283
|
+
>
|
284
|
+
<span class="sr-only">Open Application Menu</span>
|
285
|
+
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
286
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
287
|
+
</svg>
|
288
|
+
</button>
|
289
|
+
|
290
|
+
<div class="py-2 px-1">
|
291
|
+
<div class="mx-auto sm:px-6 sm:py-4 px-2">
|
292
|
+
<div class="bg-white rounded-md shadow sm:py-14 sm:px-12 py-10 px-7">
|
293
|
+
<div class="prose" style="max-width: none;">
|
294
|
+
<%= yield %>
|
295
|
+
</div>
|
296
|
+
</div>
|
297
|
+
|
298
|
+
</div>
|
299
|
+
</div>
|
300
|
+
</main>
|
301
|
+
</div>
|
302
|
+
</div>
|
303
|
+
</div>
|
304
|
+
</body>
|
305
|
+
</html>
|
@@ -0,0 +1,65 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
6
|
+
<title>Welcome to <%= t('application.name') %>!</title>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<% if content_for? :preheader %>
|
10
|
+
<span class="preheader"><% yield :preheader %></span>
|
11
|
+
<% end %>
|
12
|
+
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
|
13
|
+
<tr>
|
14
|
+
<td align="center">
|
15
|
+
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
|
16
|
+
<tr>
|
17
|
+
<td class="email-masthead">
|
18
|
+
<a href="<%= root_url %>" class="email-masthead_name">
|
19
|
+
<%= email_image_tag("light/logo/logo.png", width: image_width_for_height('light/logo/logo.png', 50), height: 50, style: "width: #{image_width_for_height('light/logo/logo.png', 50)}px; height: 50px;") %>
|
20
|
+
</a>
|
21
|
+
</td>
|
22
|
+
</tr>
|
23
|
+
<!-- Email Body -->
|
24
|
+
<tr>
|
25
|
+
<td class="email-body" width="100%" cellpadding="0" cellspacing="0">
|
26
|
+
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
|
27
|
+
<!-- Body content -->
|
28
|
+
<tr>
|
29
|
+
<td class="content-cell">
|
30
|
+
|
31
|
+
<%= yield %>
|
32
|
+
|
33
|
+
<% if false %>
|
34
|
+
<!-- Sub copy -->
|
35
|
+
<table class="body-sub">
|
36
|
+
<tr>
|
37
|
+
<td>
|
38
|
+
<p class="sub"><%= t('.trouble') %></p>
|
39
|
+
<p class="sub">{{action_url}}</p>
|
40
|
+
</td>
|
41
|
+
</tr>
|
42
|
+
</table>
|
43
|
+
<% end %>
|
44
|
+
</td>
|
45
|
+
</tr>
|
46
|
+
</table>
|
47
|
+
</td>
|
48
|
+
</tr>
|
49
|
+
<tr>
|
50
|
+
<td>
|
51
|
+
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0">
|
52
|
+
<tr>
|
53
|
+
<td class="content-cell" align="center">
|
54
|
+
<p class="sub align-center">© <%= t('.all_rights_reserved', year: Date.today.year, product_name: t('application.name')) %></p>
|
55
|
+
</td>
|
56
|
+
</tr>
|
57
|
+
</table>
|
58
|
+
</td>
|
59
|
+
</tr>
|
60
|
+
</table>
|
61
|
+
</td>
|
62
|
+
</tr>
|
63
|
+
</table>
|
64
|
+
</body>
|
65
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= @body = markdown(File.read(Rails.root.to_s + "/docs/#{@file}.md").gsub('.md)', ')')) %>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<% content_for :preheader do %>
|
2
|
+
<%= t('.preview', @values) %>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<h1><%= t('.heading', @values) %></h1>
|
6
|
+
<%= t('.body.html', @values) %>
|
7
|
+
|
8
|
+
<!-- Action -->
|
9
|
+
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
|
10
|
+
<tr>
|
11
|
+
<td align="center">
|
12
|
+
<!-- Border based button https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
|
13
|
+
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
14
|
+
<tr>
|
15
|
+
<td align="center">
|
16
|
+
<table border="0" cellspacing="0" cellpadding="0">
|
17
|
+
<tr>
|
18
|
+
<td>
|
19
|
+
<a href="<%= @cta_url %>" class="button button--" target="_blank"><%= t('.cta.label', @values) %></a>
|
20
|
+
</td>
|
21
|
+
</tr>
|
22
|
+
</table>
|
23
|
+
</td>
|
24
|
+
</tr>
|
25
|
+
</table>
|
26
|
+
</td>
|
27
|
+
</tr>
|
28
|
+
</table>
|
29
|
+
<%= t('.signature.html', @values.merge({support_email: t('application.support_email')})) %>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<% content_for :preheader do %>
|
2
|
+
<%= t('.preview', @values) %>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<h1><%= t('.heading', @values) %></h1>
|
6
|
+
<%= t('.body.html', @values) %>
|
7
|
+
|
8
|
+
<h2><%= t('.cta.heading', @values) %></h2>
|
9
|
+
<p><%= t('.cta.body', @values) %></p>
|
10
|
+
<!-- Action -->
|
11
|
+
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
|
12
|
+
<tr>
|
13
|
+
<td align="center">
|
14
|
+
<!-- Border based button https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
|
15
|
+
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
16
|
+
<tr>
|
17
|
+
<td align="center">
|
18
|
+
<table border="0" cellspacing="0" cellpadding="0">
|
19
|
+
<tr>
|
20
|
+
<td>
|
21
|
+
<a href="<%= @cta_url %>" class="button button--" target="_blank"><%= t('.cta.label', @values) %></a>
|
22
|
+
</td>
|
23
|
+
</tr>
|
24
|
+
</table>
|
25
|
+
</td>
|
26
|
+
</tr>
|
27
|
+
</table>
|
28
|
+
</td>
|
29
|
+
</tr>
|
30
|
+
</table>
|
31
|
+
<%= t('.signature.html', @values.merge({support_email: t('application.support_email')})) %>
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# Files in the config/locales directory are used for internationalization
|
2
|
+
# and are automatically loaded by Rails. If you want to use locales other
|
3
|
+
# than English, add the necessary files in this directory.
|
4
|
+
#
|
5
|
+
# To use the locales, use `I18n.t`:
|
6
|
+
#
|
7
|
+
# I18n.t "hello"
|
8
|
+
#
|
9
|
+
# In views, this is aliased to just `t`:
|
10
|
+
#
|
11
|
+
# <%= t("hello") %>
|
12
|
+
#
|
13
|
+
# To use a different locale, set it with `I18n.locale`:
|
14
|
+
#
|
15
|
+
# I18n.locale = :es
|
16
|
+
#
|
17
|
+
# This would use the information in config/locales/es.yml.
|
18
|
+
#
|
19
|
+
# The following keys must be escaped otherwise they will not be retrieved by
|
20
|
+
# the default I18n backend:
|
21
|
+
#
|
22
|
+
# true, false, on, off, yes, no
|
23
|
+
#
|
24
|
+
# Instead, surround them with single quotes.
|
25
|
+
#
|
26
|
+
# en:
|
27
|
+
# "true": "foo"
|
28
|
+
#
|
29
|
+
# To learn more, please read the Rails Internationalization guide
|
30
|
+
# available at https://guides.rubyonrails.org/i18n.html.
|
31
|
+
|
32
|
+
en:
|
33
|
+
bullet_train:
|
34
|
+
name: Bullet Train
|
35
|
+
tagline: The Ruby on Rails SaaS Template
|
36
|
+
description: Bullet Train is a Ruby on Rails SaaS-in-a-Box that saves developers weeks of effort.
|
37
|
+
keywords: bullet train ruby on rails saas template starter kit
|
38
|
+
support_email: ask@bullettrain.co
|
39
|
+
global:
|
40
|
+
or: Or
|
41
|
+
more: More
|
42
|
+
is_admin: Admin?
|
43
|
+
person: Person
|
44
|
+
admin: Admin
|
45
|
+
confirm_message: Are you sure?
|
46
|
+
typing_search: Start typing search...
|
47
|
+
sidebar: Sidebar
|
48
|
+
creator: Bullet Train, Inc.
|
49
|
+
buttons:
|
50
|
+
other: Other
|
51
|
+
debug: Debug
|
52
|
+
details: Details
|
53
|
+
revoke: Revoke
|
54
|
+
cancel: Cancel
|
55
|
+
next: Next
|
56
|
+
back: Back
|
57
|
+
new: New
|
58
|
+
edit: Edit
|
59
|
+
settings: Settings
|
60
|
+
configure: Configure
|
61
|
+
remove: Remove
|
62
|
+
delete: Delete
|
63
|
+
update: Update
|
64
|
+
sign_up: Sign Up
|
65
|
+
sign_in: Sign In
|
66
|
+
notifications:
|
67
|
+
not_found: not found
|
68
|
+
all_fields_required: All of these fields are required.
|
69
|
+
dates:
|
70
|
+
today: Today
|
71
|
+
day:
|
72
|
+
one: 1 Day
|
73
|
+
other: '%{count} Days'
|
74
|
+
last_month: Last Month
|
75
|
+
last_week: Last Week
|
76
|
+
last_day:
|
77
|
+
one: Last Day
|
78
|
+
other: Last %{count} Days
|
79
|
+
formats:
|
80
|
+
date: '%m/%d/%Y'
|
81
|
+
date_and_time: '%m/%d/%Y %l:%M %p'
|
82
|
+
|
83
|
+
menus:
|
84
|
+
main:
|
85
|
+
labels:
|
86
|
+
account: Account
|
87
|
+
new: New
|
88
|
+
profile_details: Profile Details
|
89
|
+
logout: Logout
|
90
|
+
skip: Skip For Now
|
91
|
+
dashboard: Dashboard
|
92
|
+
team_members: Team Members
|
93
|
+
webhooks: Webhooks
|
94
|
+
invitations: Invitations
|
95
|
+
billing: Billing
|
96
|
+
upgrade: Upgrade
|
97
|
+
switch_teams: Switch Teams
|
98
|
+
add_new_team: Add New Team
|
99
|
+
edit_account_details: Account Details
|
100
|
+
resources: Resources
|
101
|
+
collaborate: Collaborate
|
102
|
+
integrations: Integrations
|
103
|
+
developers: Developers
|
104
|
+
your_account: Your Account
|
105
|
+
|
106
|
+
breadcrumbs:
|
107
|
+
your_dashboard: Your Dashboard
|
108
|
+
actions:
|
109
|
+
edit: 'Edit'
|
110
|
+
new: 'New'
|
111
|
+
|
112
|
+
fields:
|
113
|
+
date_field:
|
114
|
+
apply: Apply
|
115
|
+
cancel: Clear
|
116
|
+
|
117
|
+
layouts:
|
118
|
+
|
119
|
+
application:
|
120
|
+
raise: we should never be using the application layout.
|
121
|
+
|
122
|
+
mailer:
|
123
|
+
trouble: If you’re having trouble with the button above, copy and paste the URL below into your web browser.
|
124
|
+
all_rights_reserved: "%{year} %{product_name}. All rights reserved."
|
125
|
+
|
126
|
+
public:
|
127
|
+
launch_help: We're helping people launch high quality SaaS products!
|
128
|
+
developed_by: Developed By
|
129
|
+
made_with_love: Made With Love In
|
130
|
+
location: Los Angeles, California
|
131
|
+
follow_us: Follow Us
|
132
|
+
all_rights_reserved: "%{year} Bullet Train, Inc., All Rights Reserved"
|
@@ -0,0 +1,152 @@
|
|
1
|
+
en:
|
2
|
+
activerecord:
|
3
|
+
attributes:
|
4
|
+
platform/application:
|
5
|
+
name: 'Name'
|
6
|
+
redirect_uri: 'Redirect URI'
|
7
|
+
errors:
|
8
|
+
models:
|
9
|
+
platform/application:
|
10
|
+
attributes:
|
11
|
+
redirect_uri:
|
12
|
+
fragment_present: 'cannot contain a fragment.'
|
13
|
+
invalid_uri: 'must be a valid URI.'
|
14
|
+
unspecified_scheme: 'must specify a scheme.'
|
15
|
+
relative_uri: 'must be an absolute URI.'
|
16
|
+
secured_uri: 'must be an HTTPS/SSL URI.'
|
17
|
+
forbidden_uri: 'is forbidden by the server.'
|
18
|
+
scopes:
|
19
|
+
not_match_configured: "can only contain the strings \"read\", \"write\", or \"delete\". You can also leave this field blank"
|
20
|
+
|
21
|
+
platform:
|
22
|
+
applications:
|
23
|
+
confirmations:
|
24
|
+
destroy: 'Are you sure?'
|
25
|
+
buttons:
|
26
|
+
edit: 'Edit'
|
27
|
+
destroy: 'Destroy'
|
28
|
+
submit: 'Submit'
|
29
|
+
cancel: 'Cancel'
|
30
|
+
authorize: 'Authorize'
|
31
|
+
form:
|
32
|
+
error: 'Whoops! Check your form for possible errors'
|
33
|
+
help:
|
34
|
+
confidential: 'Application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.'
|
35
|
+
redirect_uri: 'Use one line per URI'
|
36
|
+
blank_redirect_uri: "Leave it blank if you configured your provider to use Client Credentials, Resource Owner Password Credentials or any other grant type that doesn't require redirect URI."
|
37
|
+
scopes: 'Separate scopes with spaces. Leave blank to use the default scope.'
|
38
|
+
edit:
|
39
|
+
title: 'Edit application'
|
40
|
+
index:
|
41
|
+
title: 'Your applications'
|
42
|
+
new: 'New Application'
|
43
|
+
name: 'Name'
|
44
|
+
callback_url: 'Callback URL'
|
45
|
+
confidential: 'Confidential?'
|
46
|
+
actions: 'Actions'
|
47
|
+
confidentiality:
|
48
|
+
'yes': 'Yes'
|
49
|
+
'no': 'No'
|
50
|
+
new:
|
51
|
+
title: 'New Application'
|
52
|
+
show:
|
53
|
+
title: 'Application: %{name}'
|
54
|
+
application_id: 'UID'
|
55
|
+
secret: 'Secret'
|
56
|
+
secret_hashed: 'Secret hashed'
|
57
|
+
scopes: 'Scopes'
|
58
|
+
confidential: 'Confidential'
|
59
|
+
callback_urls: 'Callback urls'
|
60
|
+
actions: 'Actions'
|
61
|
+
not_defined: 'Not defined'
|
62
|
+
|
63
|
+
doorkeeper:
|
64
|
+
authorizations:
|
65
|
+
buttons:
|
66
|
+
authorize: 'Authorize'
|
67
|
+
deny: 'Deny'
|
68
|
+
error:
|
69
|
+
title: 'An error has occurred'
|
70
|
+
new:
|
71
|
+
title: 'Authorization required'
|
72
|
+
prompt: 'Authorize %{client_name} to use your account?'
|
73
|
+
able_to: 'This application will be able to'
|
74
|
+
show:
|
75
|
+
title: 'Authorization code'
|
76
|
+
|
77
|
+
authorized_applications:
|
78
|
+
confirmations:
|
79
|
+
revoke: 'Are you sure?'
|
80
|
+
buttons:
|
81
|
+
revoke: 'Revoke'
|
82
|
+
index:
|
83
|
+
title: 'Your authorized applications'
|
84
|
+
application: 'Application'
|
85
|
+
created_at: 'Created At'
|
86
|
+
date_format: '%Y-%m-%d %H:%M:%S'
|
87
|
+
|
88
|
+
pre_authorization:
|
89
|
+
status: 'Pre-authorization'
|
90
|
+
|
91
|
+
errors:
|
92
|
+
messages:
|
93
|
+
# Common error messages
|
94
|
+
invalid_request:
|
95
|
+
unknown: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
|
96
|
+
missing_param: 'Missing required parameter: %{value}.'
|
97
|
+
not_support_pkce: 'Invalid code_verifier parameter. Server does not support pkce.'
|
98
|
+
request_not_authorized: 'Request need to be authorized. Required parameter for authorizing request is missing or invalid.'
|
99
|
+
invalid_redirect_uri: "The requested redirect uri is malformed or doesn't match client redirect URI."
|
100
|
+
unauthorized_client: 'The client is not authorized to perform this request using this method.'
|
101
|
+
access_denied: 'The resource owner or authorization server denied the request.'
|
102
|
+
invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
|
103
|
+
invalid_code_challenge_method: 'The code challenge method must be plain or S256.'
|
104
|
+
server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
|
105
|
+
temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
|
106
|
+
|
107
|
+
# Configuration error messages
|
108
|
+
credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
|
109
|
+
resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfigured.'
|
110
|
+
admin_authenticator_not_configured: 'Access to admin panel is forbidden due to Doorkeeper.configure.admin_authenticator being unconfigured.'
|
111
|
+
|
112
|
+
# Access grant errors
|
113
|
+
unsupported_response_type: 'The authorization server does not support this response type.'
|
114
|
+
|
115
|
+
# Access token errors
|
116
|
+
invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'
|
117
|
+
invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'
|
118
|
+
unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'
|
119
|
+
|
120
|
+
invalid_token:
|
121
|
+
revoked: "The access token was revoked"
|
122
|
+
expired: "The access token expired"
|
123
|
+
unknown: "The access token is invalid"
|
124
|
+
revoke:
|
125
|
+
unauthorized: "You are not authorized to revoke this token"
|
126
|
+
|
127
|
+
flash:
|
128
|
+
applications:
|
129
|
+
create:
|
130
|
+
notice: 'Application created.'
|
131
|
+
destroy:
|
132
|
+
notice: 'Application deleted.'
|
133
|
+
update:
|
134
|
+
notice: 'Application updated.'
|
135
|
+
authorized_applications:
|
136
|
+
destroy:
|
137
|
+
notice: 'Application revoked.'
|
138
|
+
|
139
|
+
layouts:
|
140
|
+
admin:
|
141
|
+
title: 'Doorkeeper'
|
142
|
+
nav:
|
143
|
+
oauth2_provider: 'OAuth2 Provider'
|
144
|
+
applications: 'Applications'
|
145
|
+
home: 'Home'
|
146
|
+
application:
|
147
|
+
title: 'OAuth authorization required'
|
148
|
+
|
149
|
+
scopes:
|
150
|
+
delete: "Your OAuth application is missing the 'delete' scope to access this API method."
|
151
|
+
read: "Your OAuth application is missing the 'read' scope to access this API method."
|
152
|
+
write: "Your OAuth application is missing the 'write' scope to access this API method."
|
@@ -0,0 +1,15 @@
|
|
1
|
+
en:
|
2
|
+
stripe_connect: Stripe
|
3
|
+
# 🚅 super scaffolding will insert new oauth providers above this line.
|
4
|
+
omniauth:
|
5
|
+
team:
|
6
|
+
connected: "We've successfully added that %{provider} account to your team!"
|
7
|
+
denied: "Permission to connect to %{provider} was not granted. Not a problem! You can try again if you want to."
|
8
|
+
already_present: "That %{provider} account is already present for this team!"
|
9
|
+
not_allowed: "You're not allowed to add a %{provider} account to a team you don't have access to."
|
10
|
+
user:
|
11
|
+
connected: "We've successfully connected that %{provider} account to your user account."
|
12
|
+
reconnected: "That %{provider} account is already connected to your user account."
|
13
|
+
already_registered: "That %{provider} account is already connected to another user. Before you can connect that %{provider} account with your user account, you'll need to sign out and sign back in using that account and remove it from that user account profile."
|
14
|
+
email_already_registered: "Sorry, there is already a user registered with the email address %{email}, but this %{provider} account isn't configured for login with that account. Please sign in using your password and then add this account."
|
15
|
+
account_not_found: "Sorry, no existing account was found for that %{provider} account. In order to create a new account, please click the link in the invitation you received via email."
|
@@ -0,0 +1,36 @@
|
|
1
|
+
en:
|
2
|
+
user_mailer:
|
3
|
+
signature: &SIGNATURE
|
4
|
+
html:
|
5
|
+
<p>If you have any questions, please don't hesitate to <a href="mailto:%{support_email}">send us an email</a>.</p>
|
6
|
+
<p>Thanks,<br>Bullet Train, Inc.</p>
|
7
|
+
invited:
|
8
|
+
subject: Invitation to join %{team_name} on Bullet Train!
|
9
|
+
preview: '%{inviter_name} has sent you this invitation.'
|
10
|
+
heading: You're invited!
|
11
|
+
body:
|
12
|
+
html:
|
13
|
+
<p>
|
14
|
+
%{inviter_name} has invited you to join %{team_name} on Bullet Train.
|
15
|
+
You can join %{team_name} by clicking the button below.
|
16
|
+
</p>
|
17
|
+
cta:
|
18
|
+
label: Join %{team_name}
|
19
|
+
signature:
|
20
|
+
<<: *SIGNATURE
|
21
|
+
welcome:
|
22
|
+
subject: Welcome to the Bullet Train demo!
|
23
|
+
preview: This is the default Bullet Train welcome email. Check out the layout!
|
24
|
+
heading: Welcome to Bullet Train!
|
25
|
+
body:
|
26
|
+
html:
|
27
|
+
<p>This is the default welcome email for Bullet Train!</p>
|
28
|
+
<p>If you have any questions about Bullet Train, just reply to this email!</p>
|
29
|
+
<p>It was important to us that Bullet Train applications have a good looking, branded email template by default.</p>
|
30
|
+
<p>Thankfully our friends at <a href="https://postmarkapp.com">Postmark</a> made the foundation of this email template <a href="https://github.com/wildbit/postmark-templates">freely available</a>. From there, we've customized it slightly to better match the default Bullet Train look-and-feel.</p>
|
31
|
+
cta:
|
32
|
+
heading: What's next?
|
33
|
+
body: You can use the following link to always return to your Bullet Train account dashboard.
|
34
|
+
label: View Your Account Dashboard
|
35
|
+
signature:
|
36
|
+
<<: *SIGNATURE
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'io/wait'
|
2
|
+
|
3
|
+
module BulletTrain
|
4
|
+
class Resolver
|
5
|
+
def initialize(needle)
|
6
|
+
@needle = needle
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(eject: false, open: false, force: false, interactive: false)
|
10
|
+
# Try to figure out what kind of thing they're trying to look up.
|
11
|
+
source_file = calculate_source_file_details
|
12
|
+
|
13
|
+
if source_file[:absolute_path]
|
14
|
+
if source_file[:package_name].present?
|
15
|
+
puts ""
|
16
|
+
puts "Absolute path:".green
|
17
|
+
puts " #{source_file[:absolute_path]}".green
|
18
|
+
puts ""
|
19
|
+
puts "Package name:".green
|
20
|
+
puts " #{source_file[:package_name]}".green
|
21
|
+
puts ""
|
22
|
+
else
|
23
|
+
puts ""
|
24
|
+
puts "Project path:".green
|
25
|
+
puts " #{source_file[:project_path]}".green
|
26
|
+
puts ""
|
27
|
+
puts "Note: If this file was previously ejected from a package, we can no longer see which package it came from. However, it should say at the top of the file where it was ejected from.".yellow
|
28
|
+
puts ""
|
29
|
+
end
|
30
|
+
|
31
|
+
if interactive && !eject
|
32
|
+
puts "\nWould you like to eject the file into the local project? (y/n)\n"
|
33
|
+
input = $stdin.gets
|
34
|
+
$stdin.getc while $stdin.ready?
|
35
|
+
if input.first.downcase == 'y'
|
36
|
+
eject = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if eject
|
41
|
+
if source_file[:package_name]
|
42
|
+
if File.exist?(source_file[:project_path]) && !force
|
43
|
+
return puts "Can't eject! `#{source_file[:project_path]}` already exists!\n".red
|
44
|
+
else
|
45
|
+
`mkdir -p #{source_file[:project_path].split("/")[0...-1].join("/")}`
|
46
|
+
puts "Ejecting `#{source_file[:absolute_path]}` to `#{source_file[:project_path]}`".green
|
47
|
+
File.open("#{source_file[:project_path]}", "w+") do |file|
|
48
|
+
case source_file[:project_path].split(".").last
|
49
|
+
when "rb", "yml"
|
50
|
+
file.puts "# Ejected from `#{source_file[:package_name]}`.\n\n"
|
51
|
+
when "erb"
|
52
|
+
file.puts "<% # Ejected from `#{source_file[:package_name]}`. %>\n\n"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
`cat #{source_file[:absolute_path]} >> #{source_file[:project_path]}`.strip
|
56
|
+
end
|
57
|
+
|
58
|
+
# Just in case they try to open the file, open it from the new location.
|
59
|
+
source_file[:absolute_path] = source_file[:project_path]
|
60
|
+
else
|
61
|
+
puts "This file is already in the local project directory. Skipping ejection.".yellow
|
62
|
+
puts ""
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if interactive && !open
|
67
|
+
puts "\nWould you like to open `#{source_file[:absolute_path]}`? (y/n)\n"
|
68
|
+
input = $stdin.gets
|
69
|
+
$stdin.getc while $stdin.ready?
|
70
|
+
if input.first.downcase == 'y'
|
71
|
+
open = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if open
|
76
|
+
path = source_file[:package_name] ? source_file[:absolute_path] : "#{source_file[:project_path]}"
|
77
|
+
puts "Opening `#{path}`.\n".green
|
78
|
+
exec "open #{path}"
|
79
|
+
end
|
80
|
+
else
|
81
|
+
puts "Couldn't resolve `#{@needle}`.".red
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def calculate_source_file_details
|
86
|
+
result = {
|
87
|
+
absolute_path: nil,
|
88
|
+
project_path: nil,
|
89
|
+
package_name: nil,
|
90
|
+
}
|
91
|
+
|
92
|
+
result[:absolute_path] = class_path || partial_path || file_path
|
93
|
+
|
94
|
+
if result[:absolute_path]
|
95
|
+
base_path = "bullet_train" + result[:absolute_path].split("/bullet_train").last
|
96
|
+
|
97
|
+
# Try to calculate which package the file is from, and what it's path is within that project.
|
98
|
+
["app", "config", "lib"].each do |directory|
|
99
|
+
regex = /\/#{directory}\//
|
100
|
+
if base_path.match?(regex)
|
101
|
+
project_path = "./#{directory}/#{base_path.rpartition(regex).last}"
|
102
|
+
package_name = base_path.rpartition(regex).first.split("/").last
|
103
|
+
# If the "package name" is actually just the local project directory.
|
104
|
+
if package_name == `pwd`.chomp.split("/").last
|
105
|
+
package_name = nil
|
106
|
+
end
|
107
|
+
|
108
|
+
result[:project_path] = project_path
|
109
|
+
result[:package_name] = package_name
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
result
|
115
|
+
end
|
116
|
+
|
117
|
+
def url?
|
118
|
+
@needle.match?(/https?:\/\//)
|
119
|
+
end
|
120
|
+
|
121
|
+
def class_path
|
122
|
+
begin
|
123
|
+
@needle.constantize
|
124
|
+
return Object.const_source_location(@needle).first
|
125
|
+
rescue NameError => _
|
126
|
+
return false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def partial_path
|
131
|
+
begin
|
132
|
+
xray_path = ApplicationController.render(template: "bullet_train/partial_resolver", layout: nil, assigns: {needle: @needle}).lines[1].chomp
|
133
|
+
if xray_path.match(/<!--XRAY START \d+ (.*)-->/)
|
134
|
+
return $1
|
135
|
+
else
|
136
|
+
raise "It looks like Xray-rails isn't properly enabled?"
|
137
|
+
end
|
138
|
+
rescue ActionView::Template::Error => _
|
139
|
+
return nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def file_path
|
144
|
+
# We don't have to do anything here... the absolute path is what we're passed, and we just pass it back.
|
145
|
+
@needle
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/bullet_train/version.rb
CHANGED
data/lib/bullet_train.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'io/wait'
|
2
|
+
|
1
3
|
namespace :bullet_train do
|
2
4
|
desc "Symlink registered gems in `./tmp/gems` so their views, etc. can be inspected by Tailwind CSS."
|
3
5
|
task :link_gems => :environment do
|
@@ -23,4 +25,33 @@ namespace :bullet_train do
|
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
28
|
+
|
29
|
+
desc "Figure out where something is coming from."
|
30
|
+
task :resolve, [:all_options] => :environment do |t, arguments|
|
31
|
+
ARGV.pop while ARGV.any?
|
32
|
+
|
33
|
+
arguments[:all_options]&.split&.each do |argument|
|
34
|
+
ARGV.push(argument)
|
35
|
+
end
|
36
|
+
|
37
|
+
if ARGV.include?("--interactive")
|
38
|
+
puts "\nOK, paste what you've got for us and hit <Return>!\n".blue
|
39
|
+
|
40
|
+
input = $stdin.gets.strip
|
41
|
+
$stdin.getc while $stdin.ready?
|
42
|
+
|
43
|
+
# Extract absolute paths from XRAY comments.
|
44
|
+
if input.match(/<!--XRAY START \d+ (.*)-->/)
|
45
|
+
input = $1
|
46
|
+
end
|
47
|
+
|
48
|
+
ARGV.unshift input.strip
|
49
|
+
end
|
50
|
+
|
51
|
+
if ARGV.first.present?
|
52
|
+
BulletTrain::Resolver.new(ARGV.first).run(eject: ARGV.include?("--eject"), open: ARGV.include?("--open"), force: ARGV.include?("--force"), interactive: ARGV.include?("--interactive"))
|
53
|
+
else
|
54
|
+
$stderr.puts "\n🚅 Usage: `bin/resolve [path, partial, or URL] (--eject) (--open)`\n".blue
|
55
|
+
end
|
56
|
+
end
|
26
57
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet_train
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.29
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Culver
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -399,6 +399,7 @@ files:
|
|
399
399
|
- app/mailers/concerns/mailers/base.rb
|
400
400
|
- app/mailers/devise_mailer.rb
|
401
401
|
- app/mailers/user_mailer.rb
|
402
|
+
- app/models/concerns/current_attributes/base.rb
|
402
403
|
- app/models/concerns/invitations/base.rb
|
403
404
|
- app/models/concerns/memberships/base.rb
|
404
405
|
- app/models/concerns/records/base.rb
|
@@ -449,6 +450,7 @@ files:
|
|
449
450
|
- app/views/account/users/_form.html.erb
|
450
451
|
- app/views/account/users/edit.html.erb
|
451
452
|
- app/views/account/users/show.html.erb
|
453
|
+
- app/views/bullet_train/partial_resolver.html.erb
|
452
454
|
- app/views/devise/confirmations/new.html.erb
|
453
455
|
- app/views/devise/mailer/confirmation_instructions.html.erb
|
454
456
|
- app/views/devise/mailer/password_change.html.erb
|
@@ -466,12 +468,22 @@ files:
|
|
466
468
|
- app/views/devise/unlocks/new.html.erb
|
467
469
|
- app/views/layouts/account.html.erb
|
468
470
|
- app/views/layouts/devise.html.erb
|
471
|
+
- app/views/layouts/docs.html.erb
|
472
|
+
- app/views/layouts/mailer.html.erb
|
473
|
+
- app/views/public/home/docs.html.erb
|
474
|
+
- app/views/user_mailer/invited.html.erb
|
475
|
+
- app/views/user_mailer/welcome.html.erb
|
476
|
+
- config/locales/en/base.yml
|
469
477
|
- config/locales/en/devise.en.yml
|
478
|
+
- config/locales/en/doorkeeper.en.yml
|
470
479
|
- config/locales/en/invitations.en.yml
|
471
480
|
- config/locales/en/memberships.en.yml
|
481
|
+
- config/locales/en/oauth.en.yml
|
472
482
|
- config/locales/en/onboarding/user_details.en.yml
|
473
483
|
- config/locales/en/onboarding/user_email.en.yml
|
484
|
+
- config/locales/en/roles.en.yml
|
474
485
|
- config/locales/en/teams.en.yml
|
486
|
+
- config/locales/en/user_mailer.en.yml
|
475
487
|
- config/locales/en/users.en.yml
|
476
488
|
- config/routes.rb
|
477
489
|
- db/migrate/20161115160419_devise_create_users.rb
|
@@ -511,6 +523,7 @@ files:
|
|
511
523
|
- db/migrate/20211027002944_add_doorkeeper_application_to_users.rb
|
512
524
|
- lib/bullet_train.rb
|
513
525
|
- lib/bullet_train/engine.rb
|
526
|
+
- lib/bullet_train/resolver.rb
|
514
527
|
- lib/bullet_train/version.rb
|
515
528
|
- lib/colorizer.rb
|
516
529
|
- lib/string/emoji.rb
|