futurism 1.2.0.pre8 → 1.2.0.pre11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +24 -0
  3. data/Appraisals~ +24 -0
  4. data/CHANGELOG.md +370 -0
  5. data/Gemfile +15 -0
  6. data/Gemfile.lock +215 -0
  7. data/Gemfile~ +17 -0
  8. data/README.md +44 -9
  9. data/README.md~ +405 -0
  10. data/app/assets/javascripts/futurism.js +191 -0
  11. data/app/assets/javascripts/futurism.min.js +2 -0
  12. data/app/assets/javascripts/futurism.min.js.map +1 -0
  13. data/app/assets/javascripts/futurism.umd.js +188 -0
  14. data/app/assets/javascripts/futurism.umd.min.js +2 -0
  15. data/app/assets/javascripts/futurism.umd.min.js.map +1 -0
  16. data/{lib → app/channels}/futurism/channel.rb +1 -1
  17. data/bin/rails +25 -0
  18. data/bin/standardize +5 -0
  19. data/bin/test +5 -0
  20. data/futurism.gemspec +38 -0
  21. data/futurism.gemspec~ +30 -0
  22. data/lib/futurism/configuration.rb +21 -0
  23. data/lib/futurism/engine.rb +19 -0
  24. data/lib/futurism/helpers.rb +2 -2
  25. data/lib/futurism/importmap.rb +2 -0
  26. data/lib/futurism/version.rb +1 -1
  27. data/lib/futurism.rb +2 -1
  28. data/package.json +47 -0
  29. data/package.json~ +51 -0
  30. data/rollup.config.js +77 -0
  31. data/rollup.config.js~ +73 -0
  32. data/test/cable/channel_test.rb +319 -0
  33. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  34. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  35. data/test/dummy/app/controllers/application_controller.rb +2 -0
  36. data/test/dummy/app/controllers/home_controller.rb +6 -0
  37. data/test/dummy/app/controllers/posts_controller.rb +59 -0
  38. data/test/dummy/app/helpers/application_helper.rb +2 -0
  39. data/test/dummy/app/helpers/posts_helper.rb +2 -0
  40. data/test/dummy/app/jobs/application_job.rb +7 -0
  41. data/test/dummy/app/models/action_item.rb +2 -0
  42. data/test/dummy/app/models/application_record.rb +3 -0
  43. data/test/dummy/app/models/post.rb +2 -0
  44. data/test/dummy/config/application.rb +29 -0
  45. data/test/dummy/config/boot.rb +5 -0
  46. data/test/dummy/config/environment.rb +5 -0
  47. data/test/dummy/config/environments/development.rb +40 -0
  48. data/test/dummy/config/environments/production.rb +94 -0
  49. data/test/dummy/config/environments/test.rb +39 -0
  50. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  51. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/test/dummy/config/initializers/content_security_policy.rb +28 -0
  53. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  54. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  55. data/test/dummy/config/initializers/inflections.rb +16 -0
  56. data/test/dummy/config/initializers/mime_types.rb +4 -0
  57. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  58. data/test/dummy/config/puma.rb +38 -0
  59. data/test/dummy/config/routes.rb +12 -0
  60. data/test/dummy/config/spring.rb +6 -0
  61. data/test/dummy/db/migrate/20200711122838_create_posts.rb +9 -0
  62. data/test/dummy/db/migrate/2021042923813_create_action_items.rb +9 -0
  63. data/test/dummy/db/schema.rb +27 -0
  64. data/test/futurism_test.rb +28 -0
  65. data/test/helper/helper_test.rb +206 -0
  66. data/test/integration/navigation_test.rb +7 -0
  67. data/test/resolver/controller/renderer_test.rb +120 -0
  68. data/test/resolver/controller_test.rb +26 -0
  69. data/test/test_helper.rb +14 -0
  70. data/yarn.lock +3263 -0
  71. metadata +122 -18
  72. data/config/routes.rb +0 -2
  73. data/lib/futurism/channel.rb~ +0 -31
  74. data/lib/futurism/helpers.rb~ +0 -112
  75. data/lib/futurism/options_transformer.rb~ +0 -0
  76. data/lib/futurism/resolver/controller/renderer.rb~ +0 -76
  77. data/lib/futurism/resolver/controller.rb~ +0 -17
  78. data/lib/futurism/resolver/resources.rb~ +0 -101
  79. data/lib/futurism/version.rb~ +0 -3
  80. data/lib/futurism.rb~ +0 -30
  81. data/lib/tasks/futurism_tasks.rake +0 -39
  82. data/lib/tasks/futurism_tasks.rake~ +0 -39
data/Gemfile~ ADDED
@@ -0,0 +1,17 @@
1
+ source "https://rubygems.org"
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
4
+ # Declare your gem's dependencies in futurism.gemspec.
5
+ # Bundler will treat runtime dependencies like base dependencies, and
6
+ # development dependencies will be added by default to the :development group.
7
+ gemspec
8
+
9
+ # Declare any dependencies that are still in development here instead of in
10
+ # your gemspec. These might include edge Rails or gems from your path or
11
+ # Git. Remember to move these dependencies to your gemspec before releasing
12
+ # your gem to rubygems.org.
13
+
14
+ gem "appraisal", github: "excid3/appraisal", branch: "fix-bundle-env"
15
+
16
+ # To use a debugger
17
+ # gem 'byebug', group: [:development, :test]
data/README.md CHANGED
@@ -251,8 +251,27 @@ $ bin/rails futurism:install
251
251
  ### Manual Installation
252
252
  After `bundle`, install the Javascript library:
253
253
 
254
- ```bash
255
- $ bin/yarn add @stimulus_reflex/futurism
254
+ There are a few ways to install the Futurism JavaScript client, depending on your application setup.
255
+
256
+ #### ESBuild / Webpacker
257
+
258
+ ```sh
259
+ yarn add @stimulus_reflex/futurism
260
+ ```
261
+
262
+ #### Import maps:
263
+
264
+ ```ruby
265
+ # config/importmap.rb
266
+ # ...
267
+ pin '@stimulus_reflex/futurism', to: 'futurism.min.js', preload: true
268
+ ```
269
+
270
+ #### Rails Asset pipeline (Sprockets):
271
+
272
+ ```html+erb
273
+ <!-- app/views/layouts/application.html.erb -->
274
+ <%= javascript_include_tag "futurism.umd.min.js", "data-turbo-track": "reload" %>
256
275
  ```
257
276
 
258
277
  In your `app/javascript/channels/index.js`, add the following
@@ -308,6 +327,18 @@ Out of the box, Rails will prefix generated urls with `http://example.org` rathe
308
327
 
309
328
  to your environments.
310
329
 
330
+ ### Choosing the parent for Futurism::Channel
331
+
332
+ By default Futurism::CHannel will inherit from ApplicationCable::Channel, you can change this by setting
333
+
334
+ ```ruby
335
+ Futurism.configure do |config|
336
+ config.parent_channel = "CustomFuturismChannel"
337
+ end
338
+
339
+ ```
340
+ in config/initializers.
341
+
311
342
  ## Contributing
312
343
 
313
344
  ### Get local environment setup
@@ -343,13 +374,17 @@ cd path/to/project
343
374
  yarn install --force
344
375
  ```
345
376
 
346
- ### Release
347
-
348
- 1. Update the version numbers in `javascript/package.json` and `lib/futurism/version.rb`
349
- 2. `git commit -m "Bump version to x.x.x"`
350
- 3. Run `bundle exec rake build`
351
- 4. Run `bundle exec rake release`
352
- 5. `cd javascript && npm publish --access public`
377
+ ### 📦 Releasing
378
+
379
+ 1. Make sure that you run `yarn` and `bundle` to pick up the latest.
380
+ 2. Bump version number at `lib/futurism/version.rb`. Pre-release versions use `.preN`
381
+ 3. Run `rake build` and `yarn build`
382
+ 4. Commit and push changes to github `git commit -m "Bump version to x.x.x"`
383
+ 5. Run `rake release`
384
+ 6. Run `yarn publish --no-git-tag-version`
385
+ 7. Yarn will prompt you for the new version. Pre-release versions use `-preN`
386
+ 8. Commit and push changes to GitHub
387
+ 9. Create a new release on GitHub ([here](https://github.com/stimulusreflex/futurism/releases)) and generate the changelog for the stable release for it
353
388
 
354
389
  ## License
355
390
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/README.md~ ADDED
@@ -0,0 +1,405 @@
1
+ # Futurism
2
+ [![Twitter follow](https://img.shields.io/twitter/follow/julian_rubisch?style=social)](https://twitter.com/julian_rubisch)
3
+ <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
4
+ [![All Contributors](https://img.shields.io/badge/all_contributors-15-orange.svg?style=flat-square)](#contributors-)
5
+ <!-- ALL-CONTRIBUTORS-BADGE:END -->
6
+ Lazy-load Rails partials via CableReady
7
+
8
+ :rotating_light: *BREAKING CHANGE: With v1.0, futurism has been transferred to the [stimulusreflex](https://github.com/stimulusreflex) organization. Please update your npm package to `@stimulus_reflex/futurism` accordingly* :rotating_light:
9
+
10
+ <img src="https://user-images.githubusercontent.com/4352208/88374198-9e6f3500-cd99-11ea-804b-0216ed320eff.jpg" alt="birmingham-museums-trust-GrvC6MI-z4w-unsplash" width="50%" align="center"/>
11
+ <span>Photo by <a href="https://unsplash.com/@birminghammuseumstrust?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Birmingham Museums Trust</a> on <a href="https://unsplash.com/s/photos/futurism?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>
12
+
13
+ ## Table of Contents
14
+
15
+ - [Table of Contents](#table-of-contents)
16
+ - [Facts](#facts)
17
+ - [Browser Support](#browser-support)
18
+ - [Usage](#usage)
19
+ - [API](#api)
20
+ - [Resource](#resource)
21
+ - [Explicit Partial](#explicit-partial)
22
+ - [HTML Options](#html-options)
23
+ - [Eager Loading](#eager-loading)
24
+ - [Bypassing](#bypassing)
25
+ - [Broadcast Partials Individually](#broadcast-partials-individually)
26
+ - [Contextual Placeholder Arguments](#contextual-placeholder-arguments)
27
+ - [Events](#events)
28
+ - [Installation](#installation)
29
+ - [Manual Installation](#manual-installation)
30
+ - [Authentication](#authentication)
31
+ - [Testing](#testing)
32
+ - [Gotchas](#gotchas)
33
+ - [Contributing](#contributing)
34
+ - [License](#license)
35
+ - [Contributors](#contributors)
36
+
37
+ ## Facts
38
+ - only one dependency: CableReady
39
+ - bundle size (without CableReady) is around [~2.46kB](https://bundlephobia.com/result?p=@stimulus_reflex/futurism@0.7.2)
40
+
41
+ ### Browser Support
42
+
43
+ - Chrome v67+ (v54+ via Polyfill)
44
+ - Firefox v63+
45
+ - Edge v79+
46
+ - Safari v10.1+ via Polyfill
47
+ - iOS Safari & Chrome v10.3+ via Polyfill
48
+
49
+ [Caniuse](https://www.caniuse.com/#search=custom%20elements)
50
+
51
+ ## Usage
52
+ with a helper in your template
53
+
54
+ ```erb
55
+ <%= futurize @posts, extends: :div do %>
56
+ <!-- placeholder -->
57
+ <% end %>
58
+ ```
59
+
60
+ custom `<futurism-element>`s (in the form of a `<div>` or a `<tr is="futurism-table-row">` are rendered. Those custom elements have an `IntersectionObserver` attached that will send a signed global id to an ActionCable channel (`FuturismChannel`) which will then replace the placeholders with the actual resource partial.
61
+
62
+ With that method, you could lazy load every class that has to_partial_path defined (ActiveModel has by default).
63
+
64
+ You can pass the placeholder as a block:
65
+
66
+ ```erb
67
+ <%= futurize @posts, extends: :tr do %>
68
+ <td class="placeholder"></td>
69
+ <% end %>
70
+ ```
71
+
72
+ ![aa601dec1930151f71dbf0d6b02b61c9](https://user-images.githubusercontent.com/4352208/87131629-f768a480-c294-11ea-89a9-ea0a76ee06ef.gif)
73
+
74
+ You can also omit the placeholder, which falls back to [eager loading](#eager-loading).
75
+
76
+ ## API
77
+
78
+ Currently there are two ways to call `futurize`, designed to wrap `render`'s behavior:
79
+
80
+ ### Resource
81
+
82
+ You can pass a single `ActiveRecord` or an `ActiveRecord::Relation` to `futurize`, just as you would call `render`:
83
+
84
+ ```erb
85
+ <%= futurize @posts, extends: :tr do %>
86
+ <td class="placeholder"></td>
87
+ <% end %>
88
+ ```
89
+
90
+ #### Partial Path
91
+
92
+ Remember that you can override the partial path in you models, like so:
93
+
94
+ ```rb
95
+ class Post < ApplicationRecord
96
+ def to_partial_path
97
+ "home/post"
98
+ end
99
+ end
100
+ ```
101
+
102
+ That way you get maximal flexibility when just specifying a single resource.
103
+
104
+ ### Explicit Partial
105
+
106
+ Call `futurize` with a `partial` keyword:
107
+
108
+ ```erb
109
+ <%= futurize partial: "items/card", locals: {card: @card}, extends: :div do %>
110
+ <div class="spinner"></div>
111
+ <% end %>
112
+ ```
113
+
114
+ You can also use the shorthand syntax:
115
+
116
+ ```erb
117
+ <%= futurize "items/card", card: @card, extends: :div do %>
118
+ <div class="spinner"></div>
119
+ <% end %>
120
+ ```
121
+
122
+ #### Collections
123
+
124
+ Collection rendering is also possible:
125
+
126
+ ```erb
127
+ <%= futurize partial: "items/card", collection: @cards, extends: :div do %>
128
+ <div class="spinner"></div>
129
+ <% end %>
130
+ ```
131
+
132
+ #### Specifying Controller to Render
133
+
134
+ You can also pass in the controller that will be used to render the partial.
135
+
136
+ ```erb
137
+ <%= futurize partial: "items/card", collection: @cards, controller: MyController, extends: :div do %>
138
+ <div class="spinner"></div>
139
+ <% end %>
140
+ ```
141
+
142
+ By default (i.e. not passing in a value), futurize will use `ApplicationController`, but you may override by setting the Futurism default controller in an initializer, for example `config/initializers/futurism.rb`.
143
+
144
+ ```ruby
145
+ Futurism.default_controller = "MyController" # to avoid the controller from trying to autoload at boot, provide as a string
146
+ ```
147
+
148
+ ### HTML Options
149
+
150
+ You can pass a hash of attribute/value pairs which will be mixed into the HTML markup for the placeholder element. This is important for layouts that require elements to have dimensionality. For example, many scripts calculate size based on element height and width. This option ensures that your elements have integrity, even if they are gone before you see them.
151
+
152
+ ```erb
153
+ <%= futurize @posts, extends: :tr, html_options: {style: "width: 50px; height: 50px;"} do %>
154
+ <td class="placeholder"></td>
155
+ <% end %>
156
+ ```
157
+
158
+ This will output the following:
159
+
160
+ ```html
161
+ <tr style="width: 50px; height: 50px;">
162
+ <td class="placeholder"></td>
163
+ </tr>
164
+ ```
165
+
166
+ ### Eager Loading
167
+ It may sound surprising to support eager loading in a lazy loading library :joy:, but there's a quite simple use case:
168
+
169
+ Suppose you have some hidden interactive portion of your page, like a tab or dropdown. You don't want its content to block the initial page load, but once that is done, you occasionally don't want to wait for the element to become visible and trigger the `IntersectionObserver`, you want to lazy load its contents right after it's added to the DOM.
170
+
171
+ Futurism makes that dead simple:
172
+
173
+ ```erb
174
+ <%= futurize 'some_tab', eager: true, extends: :tr do %>
175
+ <div class="placeholder"</td>
176
+ <% end %>
177
+ ```
178
+
179
+ ### Bypassing
180
+
181
+ In some rare cases, e.g. when combined with CableReady's async `updates_for` mechanism, you'll want to bypass futurism entirely and fall back to native `rendering`. You can do this by passing an `unless` option:
182
+
183
+ ```erb
184
+ <%= futurize 'some_tab', unless: bypass_futurism?, extends: :tr do %>
185
+ <div class="placeholder"</td>
186
+ <% end %>
187
+ ```
188
+
189
+ Internally, this works the same as [bypassing futurism in tests](#testing)
190
+
191
+
192
+ ### Broadcast Partials Individually
193
+ Futurism's default behavior is to `broadcast` partials as they are generated in batches:
194
+
195
+ On the client side, `IntersectionObserver` events are triggered in a debounced fashion, so several `render`s are performed on the server for each of those events. By default, futurism will group those to a single `broadcast` call (to save server CPU time).
196
+
197
+ For collections, however, you can opt into individual broadcasts by specifying `broadcast_each: true` in your helper usage:
198
+
199
+ ```erb
200
+ <%= futurize @posts, broadcast_each: true, extends: :tr do %>
201
+ <div class="placeholder"</td>
202
+ <% end %>
203
+ ```
204
+
205
+ ### Contextual Placeholder Arguments
206
+
207
+ For individual models or arbitrary collections, you can pass `record` and `index` to the placeholder block as arguments:
208
+
209
+ ```erb
210
+ <%= futurize @post, extends: :div do |post| %>
211
+ <div><%= post.title %></div>
212
+ <% end %>
213
+ ```
214
+
215
+ ```erb
216
+ <%= futurize @posts, extends: :tr do |post, index| %>
217
+ <td><%= index + 1 %></td><td><%= post.title %></td>
218
+ <% end %>
219
+ ```
220
+
221
+ ```erb
222
+ <%= futurize partial: "users/user", collection: users, extends: "tr" do |user, index| %>
223
+ <td><%= index + 1 %></td><td><%= user.name %></td>
224
+ <% end >
225
+ ```
226
+
227
+ ## Events
228
+
229
+ Once your futurize element has been rendered, the `futurize:appeared` custom event will be called.
230
+
231
+ ## Installation
232
+ Add this line to your application's Gemfile:
233
+
234
+ ```ruby
235
+ gem 'futurism'
236
+ ```
237
+
238
+ And then execute:
239
+ ```bash
240
+ $ bundle
241
+ ```
242
+
243
+ To copy over the javascript files to your application, run
244
+
245
+ ```bash
246
+ $ bin/rails futurism:install
247
+ ```
248
+
249
+ **! Note that the installer will run `yarn add @stimulus_reflex/futurism` for you !**
250
+
251
+ ### Manual Installation
252
+ After `bundle`, install the Javascript library:
253
+
254
+ ```bash
255
+ $ bin/yarn add @stimulus_reflex/futurism
256
+ ```
257
+
258
+ In your `app/javascript/channels/index.js`, add the following
259
+
260
+ ```js
261
+ import * as Futurism from '@stimulus_reflex/futurism'
262
+
263
+ import consumer from './consumer'
264
+
265
+ Futurism.initializeElements()
266
+ Futurism.createSubscription(consumer)
267
+ ```
268
+
269
+ ## Authentication
270
+ For authentication, you can rely on ActionCable identifiers, for example, if you use Devise:
271
+
272
+ ```ruby
273
+ module ApplicationCable
274
+ class Connection < ActionCable::Connection::Base
275
+ identified_by :current_user
276
+
277
+ def connect
278
+ self.current_user = env["warden"].user || reject_unauthorized_connection
279
+ end
280
+ end
281
+ end
282
+ ```
283
+
284
+ The [Stimulus Reflex Docs](https://docs.stimulusreflex.com/authentication) have an excellent section about all sorts of authentication.
285
+
286
+ ## Testing
287
+ In Rails system tests there is a chance that flaky errors will occur due to Capybara not waiting for the placeholder elements to be replaced. To overcome this, add the flag
288
+
289
+ ```ruby
290
+ Futurism.skip_in_test = true
291
+ ```
292
+
293
+ to an initializer, for example `config/initializers/futurism.rb`.
294
+
295
+ ## Gotchas
296
+
297
+ ### ActiveStorage URLs aren't correct in development
298
+
299
+ Out of the box, Rails will prefix generated urls with `http://example.org` rather than `http://localhost`, much like ActionMailer. To amend this, add
300
+
301
+ ```ruby
302
+ # config/environments/development.rb
303
+ config.action_controller.default_url_options = {host: "localhost", port: 3000}
304
+
305
+ # config/environments/production.rb
306
+ config.action_controller.default_url_options = {host: "mysite.com"}
307
+ ```
308
+
309
+ to your environments.
310
+
311
+ ### Choosing the parent for Futurism::Channel
312
+
313
+ By default Futurism::CHannel will inherit from ApplicationCable::Channel, you can change this by setting
314
+
315
+ ```ruby
316
+ Futurism.configure do |config|
317
+ config.parent_channel = "CustomFuturismChannel"
318
+ end
319
+
320
+ ```
321
+ in config/initializers.
322
+
323
+ ## Contributing
324
+
325
+ ### Get local environment setup
326
+
327
+ Below are a set of instructions that may help you get a local development environment working
328
+
329
+ ```shell
330
+ # Get the gem/npm package source locally
331
+ git clone futurism
332
+ cd futurism/javascript
333
+ yarn install # install all of the npm package's dependencies
334
+ yarn link # set the local machine's futurism npm package's lookup to this local path
335
+
336
+ # Setup a sample project, use the information below directly or use your own project
337
+ git clone https://github.com/leastbad/stimulus_reflex_harness.git
338
+ cd stimulus_reflex_harness
339
+ git checkout futurism
340
+ # Edit Gemfile to point point to local gem (e.g. `gem "futurism", path: "../futurism"`)
341
+ # yarn link @stimulus_reflex/futurism
342
+
343
+
344
+ # Do your work, Submit PR, Profit!
345
+
346
+
347
+ # To stop using your local version of futurism
348
+ # change your Gemfile back to the published (e.g. `gem "futurism"`)
349
+ cd path/to/futurism/javascript
350
+ # Stop using the local npm package
351
+ yarn unlink
352
+
353
+ # Instruct your project to reinstall the published version of the npm package
354
+ cd path/to/project
355
+ yarn install --force
356
+ ```
357
+
358
+ ### Release
359
+
360
+ 1. Update the version numbers in `javascript/package.json` and `lib/futurism/version.rb`
361
+ 2. `git commit -m "Bump version to x.x.x"`
362
+ 3. Run `bundle exec rake build`
363
+ 4. Run `bundle exec rake release`
364
+ 5. `cd javascript && npm publish --access public`
365
+
366
+ ## License
367
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
368
+
369
+ ## Contributors ✨
370
+
371
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
372
+
373
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
374
+ <!-- prettier-ignore-start -->
375
+ <!-- markdownlint-disable -->
376
+ <table>
377
+ <tr>
378
+ <td align="center"><a href="http://www.julianrubisch.at"><img src="https://avatars0.githubusercontent.com/u/4352208?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Julian Rubisch</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=julianrubisch" title="Code">💻</a></td>
379
+ <td align="center"><a href="https://github.com/darkrubyist"><img src="https://avatars2.githubusercontent.com/u/11207292?v=4?s=100" width="100px;" alt=""/><br /><sub><b>darkrubyist</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=darkrubyist" title="Code">💻</a> <a href="https://github.com/stimulusreflex/futurism/commits?author=darkrubyist" title="Documentation">📖</a></td>
380
+ <td align="center"><a href="https://ParamagicDev.github.io/portfolio"><img src="https://avatars2.githubusercontent.com/u/26425882?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Konnor Rogers</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=ParamagicDev" title="Code">💻</a></td>
381
+ <td align="center"><a href="https://www.andrewm.codes"><img src="https://avatars1.githubusercontent.com/u/18423853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Mason</b></sub></a><br /><a href="#maintenance-andrewmcodes" title="Maintenance">🚧</a></td>
382
+ <td align="center"><a href="http://gorails.com"><img src="https://avatars1.githubusercontent.com/u/67093?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Oliver</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=excid3" title="Code">💻</a> <a href="https://github.com/stimulusreflex/futurism/pulls?q=is%3Apr+reviewed-by%3Aexcid3" title="Reviewed Pull Requests">👀</a></td>
383
+ <td align="center"><a href="https://github.com/leastbad"><img src="https://avatars2.githubusercontent.com/u/38150464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>leastbad</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=leastbad" title="Code">💻</a> <a href="https://github.com/stimulusreflex/futurism/pulls?q=is%3Apr+reviewed-by%3Aleastbad" title="Reviewed Pull Requests">👀</a></td>
384
+ <td align="center"><a href="http://code.digimonkey.com"><img src="https://avatars0.githubusercontent.com/u/74207?v=4?s=100" width="100px;" alt=""/><br /><sub><b>M. E. Patterson</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/issues?q=author%3Amepatterson" title="Bug reports">🐛</a></td>
385
+ </tr>
386
+ <tr>
387
+ <td align="center"><a href="http://fractaledmind.com"><img src="https://avatars3.githubusercontent.com/u/5077225?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stephen Margheim</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=fractaledmind" title="Code">💻</a></td>
388
+ <td align="center"><a href="http://hass.codes"><img src="https://avatars2.githubusercontent.com/u/1064205?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hassanin Ahmed</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=sas1ni69" title="Code">💻</a></td>
389
+ <td align="center"><a href="https://marcoroth.dev"><img src="https://avatars2.githubusercontent.com/u/6411752?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marco Roth</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=marcoroth" title="Code">💻</a></td>
390
+ <td align="center"><a href="https://viedit.com"><img src="https://avatars1.githubusercontent.com/u/49990587?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Viedit com</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=vieditcom" title="Documentation">📖</a></td>
391
+ <td align="center"><a href="http://scottbarrow.ca"><img src="https://avatars2.githubusercontent.com/u/5571736?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Scott Barrow</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=scottbarrow" title="Code">💻</a></td>
392
+ <td align="center"><a href="http://domchristie.co.uk"><img src="https://avatars0.githubusercontent.com/u/111734?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dom Christie</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/pulls?q=is%3Apr+reviewed-by%3Adomchristie" title="Reviewed Pull Requests">👀</a></td>
393
+ <td align="center"><a href="http://www.rickychilcott.com"><img src="https://avatars1.githubusercontent.com/u/445759?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricky Chilcott</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/pulls?q=is%3Apr+reviewed-by%3Arickychilcott" title="Reviewed Pull Requests">👀</a></td>
394
+ </tr>
395
+ <tr>
396
+ <td align="center"><a href="https://github.com/mansakondo"><img src="https://avatars.githubusercontent.com/u/47113995?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mansakondo</b></sub></a><br /><a href="https://github.com/stimulusreflex/futurism/commits?author=mansakondo" title="Code">💻</a></td>
397
+ </tr>
398
+ </table>
399
+
400
+ <!-- markdownlint-restore -->
401
+ <!-- prettier-ignore-end -->
402
+
403
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
404
+
405
+ This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!