@profoundry-us/loco_motion 0.0.5
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.
- package/LICENSE +21 -0
- package/README.md +809 -0
- package/app/components/daisy/data_display/countdown_controller.js +78 -0
- package/index.js +3 -0
- package/package.json +19 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Profoundry
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
<!-- omit from toc -->
|
|
2
|
+
# loco_motion
|
|
3
|
+
|
|
4
|
+
Crazy fast Rails development with modern tools and components leveraging
|
|
5
|
+
ViewComponent, TailwindCSS, DaisyUI and more!
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
_**DISCLAIMER**_
|
|
10
|
+
|
|
11
|
+
This project is in active development and many changes occur with every release!
|
|
12
|
+
In particular, new Daisy components are being added frequently and older
|
|
13
|
+
components are being updated with new features meaning the APIs are very likely
|
|
14
|
+
to change!
|
|
15
|
+
|
|
16
|
+
We plan to publish the docs site to a publicly available URL soon, but until
|
|
17
|
+
then, you can run the docs by cloning the repository and running `make all` (or
|
|
18
|
+
`make all-quick` if you've already run `make all` or `make rebuild` previously)
|
|
19
|
+
and visiting http://localhost:3000/ in your browser.
|
|
20
|
+
|
|
21
|
+
Please reach out by opening an
|
|
22
|
+
[Issue](https://github.com/profoundry-us/loco_motion/issues) if you've found a
|
|
23
|
+
bug or starting a
|
|
24
|
+
[Discussion](https://github.com/profoundry-us/loco_motion/discussions) if you
|
|
25
|
+
have a question!
|
|
26
|
+
|
|
27
|
+
Please open a Discussion / Issue **before** starting a Pull Request to make sure
|
|
28
|
+
we aren't already working on the suggested feature / bug, and to ensure that
|
|
29
|
+
your solution is aligned with our goals.
|
|
30
|
+
|
|
31
|
+
- [About](#about)
|
|
32
|
+
- [Getting Started](#getting-started)
|
|
33
|
+
- [Installing / Setting up Rails](#installing--setting-up-rails)
|
|
34
|
+
- [Install HAML (Optional)](#install-haml-optional)
|
|
35
|
+
- [Install DaisyUI (Optional)](#install-daisyui-optional)
|
|
36
|
+
- [Try Out Your Application](#try-out-your-application)
|
|
37
|
+
- [Debugging](#debugging)
|
|
38
|
+
- [Testing](#testing)
|
|
39
|
+
- [Authentication](#authentication)
|
|
40
|
+
- [Web Console](#web-console)
|
|
41
|
+
- [BetterErrors (Optional)](#bettererrors-optional)
|
|
42
|
+
- [LocoMotion Components](#locomotion-components)
|
|
43
|
+
- [Install](#install)
|
|
44
|
+
- [Using Components](#using-components)
|
|
45
|
+
- [Setting a Base Component Class](#setting-a-base-component-class)
|
|
46
|
+
- [Tooling](#tooling)
|
|
47
|
+
- [Next Steps](#next-steps)
|
|
48
|
+
|
|
49
|
+
## About
|
|
50
|
+
|
|
51
|
+
loco_motion is both a set of philosophies and paradigms for developing robust
|
|
52
|
+
web applications in Ruby on Rails, as well gems and tools to help you execute
|
|
53
|
+
on your vision quickly and reliably.
|
|
54
|
+
|
|
55
|
+
It includes standards for your
|
|
56
|
+
|
|
57
|
+
* Development Environment
|
|
58
|
+
* Testing / Debugging
|
|
59
|
+
* CSS / Page Markup
|
|
60
|
+
* Components / Libraries
|
|
61
|
+
* Releasing / Hosting
|
|
62
|
+
* and much more!
|
|
63
|
+
|
|
64
|
+
You can use as much or as little of the frameworks and philosophies provided,
|
|
65
|
+
and you can customize it all to your heart's content.
|
|
66
|
+
|
|
67
|
+
## Getting Started
|
|
68
|
+
|
|
69
|
+
We recommend using Docker to get your project setup from the beginning. Even
|
|
70
|
+
before you run the `rails new` command. This ensures that you have a stable
|
|
71
|
+
development environment, no matter what OS or system you're using to develop.
|
|
72
|
+
|
|
73
|
+
It also allows you to troubleshoot and debug the application since the
|
|
74
|
+
development container is so small and simple with very few dependencies.
|
|
75
|
+
|
|
76
|
+
You can download it from https://www.docker.com/.
|
|
77
|
+
|
|
78
|
+
Once you have that downloaded, open a terminal, and create a new directory for
|
|
79
|
+
your project. You can put it anywhere, but we recommend a directory structure
|
|
80
|
+
similar to the following:
|
|
81
|
+
|
|
82
|
+
```shell
|
|
83
|
+
mkdir -p ~/Development/mycompany/myproject
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Now, change into that directory:
|
|
87
|
+
|
|
88
|
+
```shell
|
|
89
|
+
cd ~/Development/mycompany/myproject
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Look in the `examples` directory for basic `docker-compose.yml`, `Dockerfile`,
|
|
93
|
+
`dev/Dockerfile`, and `entrypoint.sh` files to get you started and give you a
|
|
94
|
+
place to run commands. Copy these into your project directory.
|
|
95
|
+
|
|
96
|
+
Next, we recommend using a [Makefile](/examples/Makefile) (also in
|
|
97
|
+
`examples`) to create shortcuts for running your various commands. `make` will
|
|
98
|
+
run on just about any operating system, and provides a self-documenting list of
|
|
99
|
+
all of the ways that you typically interact with your application. This means
|
|
100
|
+
that other developers can quickly see the common use-cases, but will also have a
|
|
101
|
+
starting point if they need to customize any of the commands for their
|
|
102
|
+
particular setup.
|
|
103
|
+
|
|
104
|
+
Copy this `Makefile` into your top-level project directory as well.
|
|
105
|
+
|
|
106
|
+
Your directory structure should look like this:
|
|
107
|
+
|
|
108
|
+
```txt
|
|
109
|
+
- ~/Development
|
|
110
|
+
- mycompany
|
|
111
|
+
- myproject
|
|
112
|
+
- Dockerfile
|
|
113
|
+
- Makefile
|
|
114
|
+
- dev
|
|
115
|
+
- Dockerfile
|
|
116
|
+
- docker-compose.yml
|
|
117
|
+
- entrypoint.sh
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Finally, we recommend [VSCode](https://code.visualstudio.com/) as your code
|
|
121
|
+
editor, but this is purely preference. It has a lot of plugins that make it
|
|
122
|
+
really customizable, but utlimately, you should use whatever editor makes you
|
|
123
|
+
most comfortable during development.
|
|
124
|
+
|
|
125
|
+
You should now be able to run `make dev` in a terminal inside your project
|
|
126
|
+
directory to build and run all of the containers.
|
|
127
|
+
|
|
128
|
+
Once they have all built and started, in a separate terminal, you can run
|
|
129
|
+
`make dev-shell` to open a Bash shell into your development container.
|
|
130
|
+
|
|
131
|
+
Congratulations! You're ready to create your Rails app!
|
|
132
|
+
|
|
133
|
+
## Installing / Setting up Rails
|
|
134
|
+
|
|
135
|
+
Once you're inside of the development container, everything should be setup and
|
|
136
|
+
ready for you to install Ruby on Rails.
|
|
137
|
+
|
|
138
|
+
Change into the app directory which is mapped to your local machine and run the
|
|
139
|
+
`rails new` command:
|
|
140
|
+
|
|
141
|
+
> [!NOTE]
|
|
142
|
+
> If you want to use something other than PostgreSQL or TailwindCSS, you can
|
|
143
|
+
> change that here. These are just our recommendations.
|
|
144
|
+
|
|
145
|
+
> [!TIP]
|
|
146
|
+
> We tend to recommend that you lag behind on the latest version of Ruby as
|
|
147
|
+
> it can occassionally have issues building the Rails project. But you can
|
|
148
|
+
> swap it to the latest inside of the `dev/Dockerfile` by changing the `FROM`
|
|
149
|
+
> line at the top.
|
|
150
|
+
|
|
151
|
+
```shell
|
|
152
|
+
cd /home/app && rails new . --skip --database=postgresql --javascript=esbuild --css=tailwind
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
If you run into trouble with the above Rails command, this should get you back
|
|
156
|
+
to a good starting point without having to blow away any changes you might have
|
|
157
|
+
made to the dev files.
|
|
158
|
+
|
|
159
|
+
```shell
|
|
160
|
+
rm -rf .dockerignore .git .gitattributes .gitignore .node-version .ruby-version\
|
|
161
|
+
Gemfile README.md Rakefile app bin config config.ru
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Once complete, you should now be able to exit out of the dev container and kill
|
|
165
|
+
the running docker containers with <kbd>Ctrl-C</kbd> in the running terminal, or
|
|
166
|
+
you can open a new terminal and run `make down`.
|
|
167
|
+
|
|
168
|
+
Open the newly created `config/database.yml` file and add the following three
|
|
169
|
+
lines under the `default` key:
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
host: db
|
|
173
|
+
username: postgres
|
|
174
|
+
password: password
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Now, uncomment the `app` section in your `docker-compose.yml` file and run
|
|
178
|
+
`make app` to build the application.
|
|
179
|
+
|
|
180
|
+
After a minute or two, everything should be booted up and you should see output
|
|
181
|
+
similar to the following:
|
|
182
|
+
|
|
183
|
+
```txt
|
|
184
|
+
myproject-app-1 | == Restarting application server ==
|
|
185
|
+
myproject-app-1 | => Booting Puma
|
|
186
|
+
myproject-app-1 | => Rails 7.1.2 application starting in development
|
|
187
|
+
myproject-app-1 | => Run `bin/rails server --help` for more startup options
|
|
188
|
+
myproject-app-1 | Puma starting in single mode...
|
|
189
|
+
myproject-app-1 | * Puma version: 6.4.0 (ruby 3.3.0-p-1) ("The Eagle of Durango")
|
|
190
|
+
myproject-app-1 | * Min threads: 5
|
|
191
|
+
myproject-app-1 | * Max threads: 5
|
|
192
|
+
myproject-app-1 | * Environment: development
|
|
193
|
+
myproject-app-1 | * PID: 1
|
|
194
|
+
myproject-app-1 | * Listening on http://0.0.0.0:3000
|
|
195
|
+
myproject-app-1 | Use Ctrl-C to stop
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Congratulations!
|
|
199
|
+
|
|
200
|
+
You can now visit [http://localhost:3000](http://localhost:3000) in your web
|
|
201
|
+
browser and see your running Rails application!
|
|
202
|
+
|
|
203
|
+
### Install HAML (Optional)
|
|
204
|
+
|
|
205
|
+
While you can use the default ERB templating system that comes with Rails, we
|
|
206
|
+
highly recommend using [HAML](https://haml.info/) instead as it provides a much
|
|
207
|
+
cleaner language for your template files.
|
|
208
|
+
|
|
209
|
+
Drop this at the bottom of your `Gemfile`:
|
|
210
|
+
|
|
211
|
+
> [!NOTE]
|
|
212
|
+
> We suggest keeping your custom gems alphabetized at the bottom.
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
# App-Specific Gems
|
|
216
|
+
gem "haml-rails", "~> 2.0"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
And add the following to your `Gemfile` in the `group :development` section:
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
gem 'html2haml'
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Next, open up a Docker shell in the app container using `make app-shell` and
|
|
226
|
+
run `bundle` to install the HAML gem.
|
|
227
|
+
|
|
228
|
+
Next, open up your `tailwind.config.js` file and replace the line for `erb`
|
|
229
|
+
views with `haml` views:
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
module.exports = {
|
|
233
|
+
content: [
|
|
234
|
+
'./app/views/**/*.html.haml',
|
|
235
|
+
// ...
|
|
236
|
+
]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Finally, you can run the following command to replace all of your `.erb`
|
|
240
|
+
files with `.haml` versions:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
HAML_RAILS_DELETE_ERB=true rails haml:erb2haml
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
You should see output similar to the following:
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
--------------------------------------------------------------------------------
|
|
250
|
+
Generating HAML for app/views/layouts/application.html.erb...
|
|
251
|
+
Generating HAML for app/views/layouts/mailer.html.erb...
|
|
252
|
+
Generating HAML for app/views/layouts/mailer.text.erb...
|
|
253
|
+
--------------------------------------------------------------------------------
|
|
254
|
+
HAML generated for the following files:
|
|
255
|
+
app/views/layouts/application.html.erb
|
|
256
|
+
app/views/layouts/mailer.html.erb
|
|
257
|
+
app/views/layouts/mailer.text.erb
|
|
258
|
+
--------------------------------------------------------------------------------
|
|
259
|
+
Deleting original .erb files.
|
|
260
|
+
--------------------------------------------------------------------------------
|
|
261
|
+
Task complete!
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Install DaisyUI (Optional)
|
|
265
|
+
|
|
266
|
+
Next up, let's utilize a mighty combo for our CSS layer!
|
|
267
|
+
|
|
268
|
+
[TailwindCSS](https://tailwindcss.com/) is a utility-based CSS framework which
|
|
269
|
+
allows you to easily build your own components by piecing together the utility
|
|
270
|
+
classes that you need.
|
|
271
|
+
|
|
272
|
+
For example, to make a rounded button, you might do something like this:
|
|
273
|
+
|
|
274
|
+
```haml
|
|
275
|
+
%button.px-4.py-2.border.rounded-lg
|
|
276
|
+
My Button
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> [!IMPORTANT]
|
|
280
|
+
> We _highly_ recommend using Tailwind for every project and have already
|
|
281
|
+
> installed it as part of the `rails new` command above.
|
|
282
|
+
|
|
283
|
+
[DaisyUI](https://daisyui.com/) takes a more traditional route and provides a
|
|
284
|
+
set of classes that utilize Tailwind to create the components for you. This
|
|
285
|
+
means your button above would look more like this:
|
|
286
|
+
|
|
287
|
+
```haml
|
|
288
|
+
%button.btn
|
|
289
|
+
My Button
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
If you want pure customization or are building your own UI components from
|
|
293
|
+
scratch, we recommend that you stick with Tailwind by itself.
|
|
294
|
+
|
|
295
|
+
However, if you're working on a project and want a good starting point for UI
|
|
296
|
+
components, you might checkout DaisyUI or a simliar Tailwind-based UI library.
|
|
297
|
+
|
|
298
|
+
DaisyUI is a plugin for Tailwind, so installing it is dead simple. Just open up
|
|
299
|
+
an app shell by running `make app-shell` in the terminal and run the following
|
|
300
|
+
command:
|
|
301
|
+
|
|
302
|
+
```shell
|
|
303
|
+
yarn add daisyui@latest --dev
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Next, edit your `tailwind.config.js` file to add it as a plugin:
|
|
307
|
+
|
|
308
|
+
> [!IMPORTANT]
|
|
309
|
+
> Make sure to add a `,` to the previous line if you put it at the bottom.
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
module.exports = {
|
|
313
|
+
//...
|
|
314
|
+
plugins: [require("daisyui")],
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
> [!IMPORTANT]
|
|
319
|
+
> Moving forward, this guide will assume you have installed DaisyUI, so some of
|
|
320
|
+
> the example view files will utilize these CSS classes.
|
|
321
|
+
|
|
322
|
+
### Try Out Your Application
|
|
323
|
+
|
|
324
|
+
Now that we have everything installed and running, let's build a few simple
|
|
325
|
+
parts of a Rails application to test that everything is working properly!
|
|
326
|
+
|
|
327
|
+
By default, only the Rails application is running, but we now need to build
|
|
328
|
+
and bundle our Javascript and CSS.
|
|
329
|
+
|
|
330
|
+
Open up your `Procfile.dev` and tell the Rails server to bind to `0.0.0.0`:
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
web: env RUBY_DEBUG_OPEN=true bin/rails server -b 0.0.0.0
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Next, you'll need to update the `Dockerfile` to tell Docker how to start
|
|
337
|
+
your app using Foreman.
|
|
338
|
+
|
|
339
|
+
Change the following line:
|
|
340
|
+
|
|
341
|
+
```Dockerfile
|
|
342
|
+
CMD ["rails", "server", "-b", "0.0.0.0"]
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
to
|
|
346
|
+
|
|
347
|
+
```Dockerfile
|
|
348
|
+
CMD ["./bin/dev"]
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Since we're using Docker, you might also want to edit your `bin/setup` file
|
|
352
|
+
to automatically remove any old PID files that might be lying around from a bad
|
|
353
|
+
container shutdown.
|
|
354
|
+
|
|
355
|
+
Add the following lines right above the last few lines that restart the
|
|
356
|
+
application server:
|
|
357
|
+
|
|
358
|
+
```sh
|
|
359
|
+
puts "\n== Removing old PID files =="
|
|
360
|
+
system! "rm -rf /home/app/tmp/pids/server.pid"
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Finally, you can kill your running docker containers (either using
|
|
364
|
+
<kbd>Ctrl-C</kbd>, opening a new terminal in your project folder and running
|
|
365
|
+
`make down`, or using the Docker UI to stop all of the containers).
|
|
366
|
+
|
|
367
|
+
Now restart using `make app`.
|
|
368
|
+
|
|
369
|
+
> [!TIP]
|
|
370
|
+
> Once you have stabalized your Dockerfile and any dependencies, you can run
|
|
371
|
+
> `make app-quick` to launch the containers without rebuilding.
|
|
372
|
+
>
|
|
373
|
+
> In this case, since we changed our `Dockerfile`, we still need to use the
|
|
374
|
+
> regular `make app` command.
|
|
375
|
+
|
|
376
|
+
You should be able to test that everything is working by altering a few files so
|
|
377
|
+
you can see some custom output:
|
|
378
|
+
|
|
379
|
+
```ruby
|
|
380
|
+
# config/routes.rb
|
|
381
|
+
|
|
382
|
+
root "application#test"
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
```ruby
|
|
386
|
+
# app/controllers/application_controller.rb
|
|
387
|
+
|
|
388
|
+
class ApplicationController < ActionController::Base
|
|
389
|
+
def test
|
|
390
|
+
render html: 'Test', layout: true
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
```haml
|
|
396
|
+
# app/views/layouts/application.html.haml
|
|
397
|
+
|
|
398
|
+
# Just modify the body & yield lines to look like this
|
|
399
|
+
|
|
400
|
+
%body
|
|
401
|
+
.m-2.p-2.rounded.bg-red-400
|
|
402
|
+
= yield
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Now visit [http://localhost:3000](http://localhost:3000) and you should
|
|
406
|
+
see a red, rounded box with the word "Test"!
|
|
407
|
+
|
|
408
|
+
If you also installed, DaisyUI, we can test that as well. Add some additional
|
|
409
|
+
code to the bottom of the `application.html.haml` file:
|
|
410
|
+
|
|
411
|
+
```haml
|
|
412
|
+
# app/views/layouts/application.html.haml
|
|
413
|
+
|
|
414
|
+
# Leave the html / head code above
|
|
415
|
+
|
|
416
|
+
%body
|
|
417
|
+
.m-2.p-2.rounded.bg-red-400
|
|
418
|
+
= yield
|
|
419
|
+
|
|
420
|
+
# Add this
|
|
421
|
+
.btn
|
|
422
|
+
Test Button
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
If everything worked, you should see a gray button that changes when
|
|
426
|
+
you hover and click on it!
|
|
427
|
+
|
|
428
|
+
> [!CAUTION]
|
|
429
|
+
> Once you're done playing around with this, you should undo your changes to the
|
|
430
|
+
> layout so that it doesn't cause confusion in later parts of this guide.
|
|
431
|
+
|
|
432
|
+
## Debugging
|
|
433
|
+
|
|
434
|
+
The latest version of Rails makes it much easier to debug within a Docker
|
|
435
|
+
container as it automatically starts a remote debugger for you.
|
|
436
|
+
|
|
437
|
+
Add the word `debugger` anywhere in your code (perhaps the `test` method of your
|
|
438
|
+
`ApplicationController`), reload the page (it will look like it's hanging), and
|
|
439
|
+
then run `make app-debug` in a separate terminal.
|
|
440
|
+
|
|
441
|
+
This will connect to the remote debugger instance which will be stopped at your
|
|
442
|
+
`debugger` line.
|
|
443
|
+
|
|
444
|
+
## Testing
|
|
445
|
+
|
|
446
|
+
Before we start creating a bunch of models, controllers, and other pieces of
|
|
447
|
+
code, it's good to get a solid testing foundation in place. Rails ships with
|
|
448
|
+
[MiniTest](https://guides.rubyonrails.org/testing.html) out of the box and many
|
|
449
|
+
people prefer this as it's built-in and is essentially just Ruby code.
|
|
450
|
+
|
|
451
|
+
However, many larger teams opt to utilize [RSpec](https://rspec.info/) which is
|
|
452
|
+
a Behavior Driven Development (BDD) framework whose tests utilize the english
|
|
453
|
+
language to help you build relevant test cases. It also has a large ecosystem of
|
|
454
|
+
plugins which can accelerate your development.
|
|
455
|
+
|
|
456
|
+
Which one you choose is up to you, but after developing many applications, we
|
|
457
|
+
recommned Rspec with [factory_bot](https://github.com/thoughtbot/factory_bot)
|
|
458
|
+
and [Shoulda Matchers](https://github.com/thoughtbot/shoulda-matchers).
|
|
459
|
+
|
|
460
|
+
Finally, although both libraries offer some functionality for testing your user
|
|
461
|
+
interface, we recommend utilizing [Cypress](https://www.cypress.io/) instead as
|
|
462
|
+
it more closely mimics the real user experience in a browser and it allows you
|
|
463
|
+
to see in real-time what is happening, including in-browser debugging!
|
|
464
|
+
|
|
465
|
+
> [!NOTE]
|
|
466
|
+
> One thing to note about Cypress, however, is that it is Javascript-based and
|
|
467
|
+
> thus requires you to write tests in Javascript. If you are only famililar with
|
|
468
|
+
> Ruby, you might want to stick with Rspec or Minitest when you first start your
|
|
469
|
+
> project, and expand into using Cypress once you are comfortable learning a new
|
|
470
|
+
> lanugage / framework.
|
|
471
|
+
|
|
472
|
+
## Authentication
|
|
473
|
+
|
|
474
|
+
There are a **lot** of different ways to handle user authentication in Ruby on
|
|
475
|
+
Rails. Because of this, many gems have popped up to help you handle this. The
|
|
476
|
+
two most popular ones are [OmniAuth](https://github.com/omniauth/omniauth) and
|
|
477
|
+
[Devise](https://github.com/heartcombo/devise).
|
|
478
|
+
|
|
479
|
+
We recommend starting with OmniAuth because it has a very simple `:developer`
|
|
480
|
+
authentication strategy which will allow you to get started very quickly, and
|
|
481
|
+
it allows you to
|
|
482
|
+
[integrate with devise](https://github.com/heartcombo/devise#omniauth) or a
|
|
483
|
+
service like [Auth0](https://auth0.com/) later if you choose.
|
|
484
|
+
|
|
485
|
+
> [!TIP]
|
|
486
|
+
> You can always find the latest setup documentation on OmniAuth's README.
|
|
487
|
+
|
|
488
|
+
Add the relevant gems to your application's `Gemfile` and re-run
|
|
489
|
+
`bundle install`:
|
|
490
|
+
|
|
491
|
+
```Gemfile
|
|
492
|
+
gem 'omniauth'
|
|
493
|
+
gem "omniauth-rails_csrf_protection"
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
After that has finished, you'll need to restart your Rails server.
|
|
497
|
+
|
|
498
|
+
> [!TIP]
|
|
499
|
+
> Although you can do this by using <kbd>Ctrl-C</kbd> and re-running `make
|
|
500
|
+
> app-quick`, a faster way to restart only the web server is to create a
|
|
501
|
+
> temporary file named `restart.txt`.
|
|
502
|
+
>
|
|
503
|
+
> You can easily do this by running `touch tmp/restart.txt` in a terminal!
|
|
504
|
+
|
|
505
|
+
Next, create an OmniAuth initializer:
|
|
506
|
+
|
|
507
|
+
```ruby
|
|
508
|
+
# config/initializers/omniauth.rb
|
|
509
|
+
|
|
510
|
+
Rails.application.config.middleware.use OmniAuth::Builder do
|
|
511
|
+
provider :developer if Rails.env.development?
|
|
512
|
+
end
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
We'll need to setup a few routes:
|
|
516
|
+
|
|
517
|
+
```ruby
|
|
518
|
+
# config/routes.rb
|
|
519
|
+
|
|
520
|
+
get '/auth/:provider/callback', to: 'sessions#create'
|
|
521
|
+
get '/login', to: 'sessions#new'
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Finally, we'll need to add the relevant sessions controller and view:
|
|
525
|
+
|
|
526
|
+
```ruby
|
|
527
|
+
# app/controllers/sessions_controller.rb
|
|
528
|
+
|
|
529
|
+
class SessionsController < ApplicationController
|
|
530
|
+
def new
|
|
531
|
+
render :new
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def create
|
|
535
|
+
user_info = request.env['omniauth.auth']
|
|
536
|
+
raise user_info # Your own session management should be placed here.
|
|
537
|
+
|
|
538
|
+
session[:user_info] = user_info.to_hash
|
|
539
|
+
|
|
540
|
+
redirect_to root_path
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
```haml
|
|
546
|
+
=# app/views/sessions/new.html.haml
|
|
547
|
+
|
|
548
|
+
- if Rails.env.development?
|
|
549
|
+
= form_tag('/auth/developer', method: 'post', data: {turbo: false}) do
|
|
550
|
+
%button.btn{ type: 'submit' }
|
|
551
|
+
Login with Developer
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
From here, you can login by visiting http://localhost:3000/login, clicking the
|
|
555
|
+
button, and entering a random name and email address.
|
|
556
|
+
|
|
557
|
+
It should throw an error and show you the line that it failed on
|
|
558
|
+
(`raise user_info`).
|
|
559
|
+
|
|
560
|
+
This is not terribly helpful as you can't easily inspect the variable and see
|
|
561
|
+
it's value.
|
|
562
|
+
|
|
563
|
+
In general, you'd want to set this to something like `session[:user_info]` and
|
|
564
|
+
integrate it into your application flow.
|
|
565
|
+
|
|
566
|
+
When you're ready for it to work, just delete or comment out the
|
|
567
|
+
`raise user_info` line.
|
|
568
|
+
|
|
569
|
+
However, this gives us an opportune time to get some better error management.
|
|
570
|
+
So let's do that first!
|
|
571
|
+
|
|
572
|
+
## Web Console
|
|
573
|
+
|
|
574
|
+
At this point, if you look in your Docker logs, you'll probably see a line like
|
|
575
|
+
the following:
|
|
576
|
+
|
|
577
|
+
```text
|
|
578
|
+
Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
> [!NOTE]
|
|
582
|
+
> Your IP address may be different! Take note of what IP the error says.
|
|
583
|
+
|
|
584
|
+
Because we're running inside Docker, we have a different network than what Rails
|
|
585
|
+
typically expects (127.0.0.1) and it blocks the default web console that loads
|
|
586
|
+
when an error happens.
|
|
587
|
+
|
|
588
|
+
This is easy to fix, we just need to take the IP address in the error message
|
|
589
|
+
above and add the following line to our `config/environments/development.rb`
|
|
590
|
+
file:
|
|
591
|
+
|
|
592
|
+
```ruby
|
|
593
|
+
# Fix console permissions for Docker
|
|
594
|
+
config.web_console.permissions = '172.23.0.1'
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Restart the application and refresh the page. You should see the same error
|
|
598
|
+
appear, but now, you should see a black console at the bottom of the screen that
|
|
599
|
+
allows you to interact with the application.
|
|
600
|
+
|
|
601
|
+
Type the following in the console and hit enter:
|
|
602
|
+
|
|
603
|
+
```ruby
|
|
604
|
+
user_info.to_hash
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
You should see some information about the user you just logged in with.
|
|
608
|
+
|
|
609
|
+
You can run pretty much any code in this console that you would run inside your
|
|
610
|
+
controllers, views, models, etc.
|
|
611
|
+
|
|
612
|
+
In fact, when I'm debugging an issue, I often find a point just above where I'm
|
|
613
|
+
wanting to look, type something non-existant like `asdf` in my Rails code, and
|
|
614
|
+
then refresh the page.
|
|
615
|
+
|
|
616
|
+
This will stop the application where the `asdf` was found and allows you to
|
|
617
|
+
interact with your application and see exactly what's going on.
|
|
618
|
+
|
|
619
|
+
## BetterErrors (Optional)
|
|
620
|
+
|
|
621
|
+
[BetterErrors](https://github.com/BetterErrors/better_errors) provides (in our
|
|
622
|
+
humble opinion) a slightly better interface for the errors that sometimes happen
|
|
623
|
+
in a Rails application.
|
|
624
|
+
|
|
625
|
+
In particular, we like how it lays out the stack trace to the left of the code
|
|
626
|
+
and console and adds a bit more styling to the page to make it easier to read.
|
|
627
|
+
|
|
628
|
+
It's also very easy to install!
|
|
629
|
+
|
|
630
|
+
Add the following to your `Gemfile` and re-run `bundle install` inside of the
|
|
631
|
+
Docker app container (`make app-shell`).
|
|
632
|
+
|
|
633
|
+
> [!TIP]
|
|
634
|
+
> You can also just kill (using <kbd>Ctrl-C</kbd>) and restart the container
|
|
635
|
+
> using `make app-quick` as this process attempts to install any gems for you.
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
```Gemfile
|
|
639
|
+
# Gemfile
|
|
640
|
+
|
|
641
|
+
group :development do
|
|
642
|
+
gem "better_errors"
|
|
643
|
+
gem "binding_of_caller"
|
|
644
|
+
end
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
> [!IMPORTANT]
|
|
648
|
+
> It is imperitive that you put these in the `:development` tag so that they
|
|
649
|
+
> cannot load in production.
|
|
650
|
+
>
|
|
651
|
+
> This would lead to a **massive** security risk!
|
|
652
|
+
|
|
653
|
+
Again, because we're running inside of Docker, we'll need to tell BetterErrors
|
|
654
|
+
that it's allowed to render for our IP address.
|
|
655
|
+
|
|
656
|
+
Add the following to the `config/environments/development.rb` file (make sure
|
|
657
|
+
the IP address matches the one you used for the Web Console above):
|
|
658
|
+
|
|
659
|
+
```ruby
|
|
660
|
+
# Allow BetterErrors to render
|
|
661
|
+
BetterErrors::Middleware.allow_ip! '172.23.0.1'
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
## LocoMotion Components
|
|
665
|
+
|
|
666
|
+
In addition to the recommendations / suggestions above, LocoMotion also provides
|
|
667
|
+
a full set of UI components to help you build robust and full-featured apps.
|
|
668
|
+
|
|
669
|
+
> [!CAUTION]
|
|
670
|
+
> The LocoMotion components are being actively developed and are NOT ready for
|
|
671
|
+
> production / public use (currently they are just some example components while
|
|
672
|
+
> I get everything setup). I'm mainly adding the docs here so that I remember
|
|
673
|
+
> how to set them up properly when they are ready for release.
|
|
674
|
+
|
|
675
|
+
### Install
|
|
676
|
+
|
|
677
|
+
Add the following to your `Gemfile` and re-run `bundle`:
|
|
678
|
+
|
|
679
|
+
```Gemfile
|
|
680
|
+
# Gemfile
|
|
681
|
+
|
|
682
|
+
gem "loco_motion", github: "profoundry-us/loco_motion", branch: "main"
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
Next add the following line to the `contents` section of your
|
|
686
|
+
`tailwind.config.js` file (make sure to change the version number to the one
|
|
687
|
+
you install):
|
|
688
|
+
|
|
689
|
+
```js
|
|
690
|
+
content:[
|
|
691
|
+
`${process.env.GEM_HOME}/loco_motion-0.0.4/app/components/**/*.{rb,js,html.haml}`,
|
|
692
|
+
|
|
693
|
+
// ...
|
|
694
|
+
]
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
> [!WARNING]
|
|
698
|
+
> Note that this will not output anything if it fails to find the right
|
|
699
|
+
> directory, so your CSS may stop working if you update the gem and forget to
|
|
700
|
+
> update this setting.
|
|
701
|
+
|
|
702
|
+
### Using Components
|
|
703
|
+
|
|
704
|
+
Back in the `app/layouts/application.html.haml` file, replace the `body` with
|
|
705
|
+
the following code and refresh your page.
|
|
706
|
+
|
|
707
|
+
```haml
|
|
708
|
+
%body
|
|
709
|
+
.m-2.p-2.rounded.bg-red-400
|
|
710
|
+
= yield
|
|
711
|
+
|
|
712
|
+
.btn
|
|
713
|
+
= LocoMotion.hello_world
|
|
714
|
+
|
|
715
|
+
%div
|
|
716
|
+
= render(LocoMotion::Buttons::ButtonComponent.new)
|
|
717
|
+
|
|
718
|
+
%div
|
|
719
|
+
= render(LocoMotion::Buttons::FabComponent.new)
|
|
720
|
+
|
|
721
|
+
%div
|
|
722
|
+
= session[:user_info].inspect
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
You should see a gray button that says "Hello World!" and the user info that
|
|
726
|
+
we saved from OmniAuth represented as a Ruby hash! You should also see the
|
|
727
|
+
example Button and Fab components.
|
|
728
|
+
|
|
729
|
+
### Setting a Base Component Class
|
|
730
|
+
|
|
731
|
+
Sometimes, you may want to override the way that LocoMotion handles things, or
|
|
732
|
+
provide some functionality yourself in a sub-class of our components. Since you
|
|
733
|
+
can't have a class inherit from two classes, we give you a way to override the
|
|
734
|
+
base class that all of our components inherit from.
|
|
735
|
+
|
|
736
|
+
This allows you to define a class that inherits from `LocoMotion::BaseComponent`
|
|
737
|
+
and then adds any special methods or overrides to our default components.
|
|
738
|
+
|
|
739
|
+
Create a file called `app/components/application_component.rb` with the following
|
|
740
|
+
contents:
|
|
741
|
+
|
|
742
|
+
```ruby
|
|
743
|
+
class ApplicationComponent < LocoMotion::BaseComponent
|
|
744
|
+
end
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
Then add the following to `config/initializers/loco_motion.rb`.
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
```ruby
|
|
751
|
+
LocoMotion.configure do |config|
|
|
752
|
+
|
|
753
|
+
# Override the base component class to inherit from our ApplicationComponent
|
|
754
|
+
# so that we can add our own overrides / methods.
|
|
755
|
+
Rails.application.config.after_initialize do
|
|
756
|
+
config.base_component_class = ApplicationComponent
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
end
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
> [!NOTE]
|
|
763
|
+
> It doesn't have to inherit from `ApplicationComponent`, you can use any class
|
|
764
|
+
> you want, so you could create a separate `CustomizedLocoMotionComponent` class
|
|
765
|
+
> so that you don't have any conflicts with your `ApplicationComponent`.
|
|
766
|
+
|
|
767
|
+
## Tooling
|
|
768
|
+
|
|
769
|
+
For VSCode, you may want to add the following to your settings to get
|
|
770
|
+
TailwindCSS Intellisense working properly.
|
|
771
|
+
|
|
772
|
+
```json
|
|
773
|
+
"tailwindCSS.emmetCompletions": true,
|
|
774
|
+
"tailwindCSS.includeLanguages": {
|
|
775
|
+
"haml": "html",
|
|
776
|
+
"ruby": "html",
|
|
777
|
+
},
|
|
778
|
+
"files.associations": {
|
|
779
|
+
"*.html.haml": "haml"
|
|
780
|
+
},
|
|
781
|
+
"tailwindCSS.experimental.classRegex": [
|
|
782
|
+
[ "add_css\\(:[a-z]+, ?\"([^\"]*)\"", "([a-zA-Z0-9\\-:]+)" ],
|
|
783
|
+
[ "css: ?\"([^\"]*)\"", "([a-zA-Z0-9\\-:]+)" ],
|
|
784
|
+
[ "class: ?\"([^\"]*)\"", "([a-zA-Z0-9\\-:]+)" ],
|
|
785
|
+
[ "(\\.[\\w\\-.]+)[\\n\\=\\{\\s]", "([\\w\\-]+)" ],
|
|
786
|
+
],
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
## Next Steps
|
|
790
|
+
|
|
791
|
+
TODO: Expand upon loco_motion components, Daisy-rails gems, icons, pagination
|
|
792
|
+
gems, etc
|
|
793
|
+
|
|
794
|
+
- [ ] Get YARD docs rendering with (better) Markdown
|
|
795
|
+
- [x] Extract relevant pieces into a yard-loco_motion plugin
|
|
796
|
+
- [ ] Publish Gem and NPM packages with only the files those need
|
|
797
|
+
- [ ] Create a new YARD plugin to document `@part`s
|
|
798
|
+
- [ ] Extract alerts into a doc component (and / or the Daisy component)
|
|
799
|
+
|
|
800
|
+
# Developing
|
|
801
|
+
|
|
802
|
+
Might need to `make demo-shell` and then `cd /home/loco_motion` and `yard link`.
|
|
803
|
+
|
|
804
|
+
Then, `cd /home/loco_demo` and run `yarn link "loco_motion"` so that you can
|
|
805
|
+
more easily do development on the various parts without having to re-run `yarn`
|
|
806
|
+
every time.
|
|
807
|
+
|
|
808
|
+
Also may need to run `yarn` on the top level directory. Maybe we can move this
|
|
809
|
+
into the Docker install / setup?
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["days", "hours", "minutes", "seconds"]
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
this.days = this.getPartValue("days")
|
|
8
|
+
this.hours = this.getPartValue("hours")
|
|
9
|
+
this.minutes = this.getPartValue("minutes")
|
|
10
|
+
this.seconds = this.getPartValue("seconds")
|
|
11
|
+
|
|
12
|
+
this.initialSeconds =
|
|
13
|
+
this.days * 24 * 60 * 60 +
|
|
14
|
+
this.hours * 60 * 60 +
|
|
15
|
+
this.minutes * 60 +
|
|
16
|
+
this.seconds
|
|
17
|
+
|
|
18
|
+
this.totalSeconds = this.initialSeconds
|
|
19
|
+
|
|
20
|
+
this.startCountdown()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getPartValue(part) {
|
|
24
|
+
let target = null
|
|
25
|
+
|
|
26
|
+
switch (part) {
|
|
27
|
+
case "days":
|
|
28
|
+
target = this.hasDaysTarget ? this.daysTarget : null
|
|
29
|
+
break
|
|
30
|
+
case "hours":
|
|
31
|
+
target = this.hasHoursTarget ? this.hoursTarget : null
|
|
32
|
+
break
|
|
33
|
+
case "minutes":
|
|
34
|
+
target = this.hasMinutesTarget ? this.minutesTarget : null
|
|
35
|
+
break
|
|
36
|
+
case "seconds":
|
|
37
|
+
target = this.hasSecondsTarget ? this.secondsTarget : null
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return target?.querySelector("span")?.style?.getPropertyValue("--value") || 0
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
startCountdown() {
|
|
45
|
+
this.interval = setInterval(() => {
|
|
46
|
+
this.totalSeconds--
|
|
47
|
+
|
|
48
|
+
if (this.totalSeconds <= 0) {
|
|
49
|
+
clearInterval(this.interval)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.updateCountdown()
|
|
53
|
+
}, 1000)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateCountdown() {
|
|
57
|
+
let days = Math.floor(this.totalSeconds / (60 * 60 * 24))
|
|
58
|
+
let hours = Math.floor((this.totalSeconds % (60 * 60 * 24)) / (60 * 60))
|
|
59
|
+
let minutes = Math.floor((this.totalSeconds % (60 * 60)) / 60)
|
|
60
|
+
let seconds = Math.floor(this.totalSeconds % 60)
|
|
61
|
+
|
|
62
|
+
if (this.hasDaysTarget) {
|
|
63
|
+
this.daysTarget?.querySelector("span")?.style?.setProperty("--value", days)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this.hasHoursTarget) {
|
|
67
|
+
this.hoursTarget?.querySelector("span")?.style?.setProperty("--value", hours)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.hasMinutesTarget) {
|
|
71
|
+
this.minutesTarget?.querySelector("span")?.style?.setProperty("--value", minutes)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (this.hasSecondsTarget) {
|
|
75
|
+
this.secondsTarget?.querySelector("span")?.style?.setProperty("--value", seconds)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@profoundry-us/loco_motion",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "Crazy fast Rails development!",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/profoundry-us/loco_motion.git"
|
|
9
|
+
},
|
|
10
|
+
"author": "Topher Fangio",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@hotwired/stimulus": "^3.2.2"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"app/components/daisy/data_display/countdown_controller.js"
|
|
18
|
+
]
|
|
19
|
+
}
|