futurism 0.7.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 010431b087816c5ad0289d895d9206e6fc88382fa1f291fc4deb36d2c2910b42
4
- data.tar.gz: ea1d6fd1574cabc5e91dc0d81dba80d0f64d05e9efdaf928614a457f2cc4b1d5
3
+ metadata.gz: 591fcd6ac1487dba8088003e9890f285336699ca85e4c54a42d8cc870c75a094
4
+ data.tar.gz: 1acc85f2deb6f879104a8010581a42eeed05692154ef0fba7fc0f0858d88afcd
5
5
  SHA512:
6
- metadata.gz: 399ad5f2f189e569eeb031b3976137d5f9d66ee1a46c0c70ec5dd1e421817c99d525755bb16dd73146d49c15392cd2e6ea75e9f2fe44f82878b7a56e996856f8
7
- data.tar.gz: bcd8cf6da3d6ce6557dee1d1dcb429beb49642bfaa9753a24d122265e43350022cf5bdd48b259732b4a91a7bb18d266feaa9a2819c800f8709f84eae5c9b86f9
6
+ metadata.gz: f037cca43c70ba733a81fe366b6af3885e262318cf3333194e756a7593d9e7df729bc651e6b37071607fe954f2baf77fac2eb7c6f027cd94daa2a29dc5d99df8
7
+ data.tar.gz: cd1ae23d900d3e594cbb4f351afda32e96847eb3be9d263559f6540c1b306b24675315d87c4575cf17d60c4ed72675786a19e150ed5534ad18e98b29754c3b58
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # Futurism
2
2
  [![Twitter follow](https://img.shields.io/twitter/follow/julian_rubisch?style=social)](https://twitter.com/julian_rubisch)
3
3
  <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
4
- [![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-)
4
+ [![All Contributors](https://img.shields.io/badge/all_contributors-15-orange.svg?style=flat-square)](#contributors-)
5
5
  <!-- ALL-CONTRIBUTORS-BADGE:END -->
6
6
  Lazy-load Rails partials via CableReady
7
7
 
8
- :rotating_light: *Futurism is still in pre-1.0 state. As much as I hope to keep the API backwards-compatible, I cannot guarantee it* :rotating_light:
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
9
 
10
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
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>
@@ -21,6 +21,8 @@ Lazy-load Rails partials via CableReady
21
21
  - [Explicit Partial](#explicit-partial)
22
22
  - [HTML Options](#html-options)
23
23
  - [Eager Loading](#eager-loading)
24
+ - [Broadcast Partials Individually](#broadcast-partials-individually)
25
+ - [Contextual Placeholder Arguments](#contextual-placeholder-arguments)
24
26
  - [Events](#events)
25
27
  - [Installation](#installation)
26
28
  - [Manual Installation](#manual-installation)
@@ -33,7 +35,7 @@ Lazy-load Rails partials via CableReady
33
35
 
34
36
  ## Facts
35
37
  - only one dependency: CableReady
36
- - bundle size (without CableReady) is around [~1.04kB](https://bundlephobia.com/result?p=@minthesize/futurism@0.1.3)
38
+ - bundle size (without CableReady) is around [~2.46kB](https://bundlephobia.com/result?p=@stimulus_reflex/futurism@0.7.2)
37
39
 
38
40
  ### Browser Support
39
41
 
@@ -68,6 +70,8 @@ You can pass the placeholder as a block:
68
70
 
69
71
  ![aa601dec1930151f71dbf0d6b02b61c9](https://user-images.githubusercontent.com/4352208/87131629-f768a480-c294-11ea-89a9-ea0a76ee06ef.gif)
70
72
 
73
+ You can also omit the placeholder, which falls back to [eager loading](#eager-loading).
74
+
71
75
  ## API
72
76
 
73
77
  Currently there are two ways to call `futurize`, designed to wrap `render`'s behavior:
@@ -171,6 +175,41 @@ Futurism makes that dead simple:
171
175
  <% end %>
172
176
  ```
173
177
 
178
+ ### Broadcast Partials Individually
179
+ Futurism's default behavior is to `broadcast` partials as they are generated in batches:
180
+
181
+ 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).
182
+
183
+ For collections, however, you can opt into individual broadcasts by specifying `broadcast_each: true` in your helper usage:
184
+
185
+ ```erb
186
+ <%= futurize @posts, broadcast_each: true, extends: :tr do %>
187
+ <div class="placeholder"</td>
188
+ <% end %>
189
+ ```
190
+
191
+ ## Contextual Placeholder Arguments
192
+
193
+ For individual models or arbitrary collections, you can pass `record` and `index` to the placeholder block as arguments:
194
+
195
+ ```erb
196
+ <%= futurize @post, extends: :div do |post| %>
197
+ <div><%= post.title %></div>
198
+ <% end %>
199
+ ```
200
+
201
+ ```erb
202
+ <%= futurize @posts, extends: :tr do |post, index| %>
203
+ <td><%= index + 1 %></td><td><%= post.title %></td>
204
+ <% end %>
205
+ ```
206
+
207
+ ```erb
208
+ <%= futurize partial: "users/user", collection: users, extends: "tr" do |user, index| %>
209
+ <td><%= index + 1 %></td><td><%= user.name %></td>
210
+ <% end >
211
+ ```
212
+
174
213
  ## Events
175
214
 
176
215
  Once your futurize element has been rendered, the `futurize:appeared` custom event will be called.
@@ -193,19 +232,19 @@ To copy over the javascript files to your application, run
193
232
  $ bin/rails futurism:install
194
233
  ```
195
234
 
196
- **! Note that the installer will run `yarn add @minthesize/futurism` for you !**
235
+ **! Note that the installer will run `yarn add @stimulus_reflex/futurism` for you !**
197
236
 
198
237
  ### Manual Installation
199
238
  After `bundle`, install the Javascript library:
200
239
 
201
240
  ```bash
202
- $ bin/yarn add @minthesize/futurism
241
+ $ bin/yarn add @stimulus_reflex/futurism
203
242
  ```
204
243
 
205
244
  In your `app/javascript/channels/index.js`, add the following
206
245
 
207
246
  ```js
208
- import * as Futurism from '@minthesize/futurism'
247
+ import * as Futurism from '@stimulus_reflex/futurism'
209
248
 
210
249
  import consumer from './consumer'
211
250
 
@@ -273,7 +312,7 @@ git clone https://github.com/leastbad/stimulus_reflex_harness.git
273
312
  cd stimulus_reflex_harness
274
313
  git checkout futurism
275
314
  # Edit Gemfile to point point to local gem (e.g. `gem "futurism", path: "../futurism"`)
276
- # yarn link @minthesize/futurism
315
+ # yarn link @stimulus_reflex/futurism
277
316
 
278
317
 
279
318
  # Do your work, Submit PR, Profit!
@@ -290,6 +329,14 @@ cd path/to/project
290
329
  yarn install --force
291
330
  ```
292
331
 
332
+ ### Release
333
+
334
+ 1. Update the version numbers in `javascript/package.json` and `lib/futurism/version.rb`
335
+ 2. `git commit -m "Bump version to x.x.x"`
336
+ 3. Run `bundle exec rake build`
337
+ 4. Run `bundle exec rake release`
338
+ 5. `cd javascript && npm publish --access public`
339
+
293
340
  ## License
294
341
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
295
342
 
@@ -302,27 +349,31 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
302
349
  <!-- markdownlint-disable -->
303
350
  <table>
304
351
  <tr>
305
- <td align="center"><a href="http://www.julianrubisch.at"><img src="https://avatars0.githubusercontent.com/u/4352208?v=4" width="100px;" alt=""/><br /><sub><b>Julian Rubisch</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=julianrubisch" title="Code">💻</a></td>
306
- <td align="center"><a href="https://github.com/darkrubyist"><img src="https://avatars2.githubusercontent.com/u/11207292?v=4" width="100px;" alt=""/><br /><sub><b>darkrubyist</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=darkrubyist" title="Code">💻</a> <a href="https://github.com/julianrubisch/futurism/commits?author=darkrubyist" title="Documentation">📖</a></td>
307
- <td align="center"><a href="https://ParamagicDev.github.io/portfolio"><img src="https://avatars2.githubusercontent.com/u/26425882?v=4" width="100px;" alt=""/><br /><sub><b>Konnor Rogers</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=ParamagicDev" title="Code">💻</a></td>
308
- <td align="center"><a href="https://www.andrewm.codes"><img src="https://avatars1.githubusercontent.com/u/18423853?v=4" width="100px;" alt=""/><br /><sub><b>Andrew Mason</b></sub></a><br /><a href="#maintenance-andrewmcodes" title="Maintenance">🚧</a></td>
309
- <td align="center"><a href="http://gorails.com"><img src="https://avatars1.githubusercontent.com/u/67093?v=4" width="100px;" alt=""/><br /><sub><b>Chris Oliver</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=excid3" title="Code">💻</a> <a href="https://github.com/julianrubisch/futurism/pulls?q=is%3Apr+reviewed-by%3Aexcid3" title="Reviewed Pull Requests">👀</a></td>
310
- <td align="center"><a href="https://github.com/leastbad"><img src="https://avatars2.githubusercontent.com/u/38150464?v=4" width="100px;" alt=""/><br /><sub><b>leastbad</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=leastbad" title="Code">💻</a> <a href="https://github.com/julianrubisch/futurism/pulls?q=is%3Apr+reviewed-by%3Aleastbad" title="Reviewed Pull Requests">👀</a></td>
311
- <td align="center"><a href="http://code.digimonkey.com"><img src="https://avatars0.githubusercontent.com/u/74207?v=4" width="100px;" alt=""/><br /><sub><b>M. E. Patterson</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/issues?q=author%3Amepatterson" title="Bug reports">🐛</a></td>
352
+ <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>
353
+ <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>
354
+ <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>
355
+ <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>
356
+ <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>
357
+ <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>
358
+ <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>
312
359
  </tr>
313
360
  <tr>
314
- <td align="center"><a href="http://fractaledmind.com"><img src="https://avatars3.githubusercontent.com/u/5077225?v=4" width="100px;" alt=""/><br /><sub><b>Stephen Margheim</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=fractaledmind" title="Code">💻</a></td>
315
- <td align="center"><a href="http://hass.codes"><img src="https://avatars2.githubusercontent.com/u/1064205?v=4" width="100px;" alt=""/><br /><sub><b>Hassanin Ahmed</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=sas1ni69" title="Code">💻</a></td>
316
- <td align="center"><a href="https://marcoroth.dev"><img src="https://avatars2.githubusercontent.com/u/6411752?v=4" width="100px;" alt=""/><br /><sub><b>Marco Roth</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=marcoroth" title="Code">💻</a></td>
317
- <td align="center"><a href="https://viedit.com"><img src="https://avatars1.githubusercontent.com/u/49990587?v=4" width="100px;" alt=""/><br /><sub><b>Viedit com</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=vieditcom" title="Documentation">📖</a></td>
318
- <td align="center"><a href="http://scottbarrow.ca"><img src="https://avatars2.githubusercontent.com/u/5571736?v=4" width="100px;" alt=""/><br /><sub><b>Scott Barrow</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/commits?author=scottbarrow" title="Code">💻</a></td>
319
- <td align="center"><a href="http://domchristie.co.uk"><img src="https://avatars0.githubusercontent.com/u/111734?v=4" width="100px;" alt=""/><br /><sub><b>Dom Christie</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/pulls?q=is%3Apr+reviewed-by%3Adomchristie" title="Reviewed Pull Requests">👀</a></td>
320
- <td align="center"><a href="http://www.rickychilcott.com"><img src="https://avatars1.githubusercontent.com/u/445759?v=4" width="100px;" alt=""/><br /><sub><b>Ricky Chilcott</b></sub></a><br /><a href="https://github.com/julianrubisch/futurism/pulls?q=is%3Apr+reviewed-by%3Arickychilcott" title="Reviewed Pull Requests">👀</a></td>
361
+ <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>
362
+ <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>
363
+ <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>
364
+ <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>
365
+ <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>
366
+ <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>
367
+ <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>
368
+ </tr>
369
+ <tr>
370
+ <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>
321
371
  </tr>
322
372
  </table>
323
373
 
324
- <!-- markdownlint-enable -->
374
+ <!-- markdownlint-restore -->
325
375
  <!-- prettier-ignore-end -->
376
+
326
377
  <!-- ALL-CONTRIBUTORS-LIST:END -->
327
378
 
328
379
  This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
@@ -1,7 +1,6 @@
1
1
  module Futurism
2
2
  class Channel < ActionCable::Channel::Base
3
3
  include CableReady::Broadcaster
4
- include Futurism::MessageVerifier
5
4
 
6
5
  def stream_name
7
6
  ids = connection.identifiers.map { |identifier| send(identifier).try(:id) || send(identifier) }
@@ -16,38 +15,19 @@ module Futurism
16
15
  end
17
16
 
18
17
  def receive(data)
19
- resources = data.fetch_values("signed_params", "sgids", "signed_controllers", "urls") { |_key| Array.new(data["signed_params"].length, nil) }.transpose
20
-
21
- resources.each do |signed_params, sgid, signed_controller, url|
22
- selector = "[data-signed-params='#{signed_params}']"
23
- selector << "[data-sgid='#{sgid}']" if sgid.present?
24
-
25
- controller = Resolver::Controller.from(signed_string: signed_controller)
26
- renderer = Resolver::Controller::Renderer.for(controller: controller,
27
- connection: connection,
28
- url: url,
29
- params: @params)
30
-
31
- resource = lookup_resource(signed_params: signed_params, sgid: sgid)
32
- html = renderer.render(resource)
18
+ resources = data.fetch_values("signed_params", "sgids", "signed_controllers", "urls", "broadcast_each") { |_key| Array.new(data["signed_params"].length, nil) }.transpose
33
19
 
20
+ resolver = Resolver::Resources.new(resource_definitions: resources, connection: connection, params: @params)
21
+ resolver.resolve do |selector, html, broadcast_each|
34
22
  cable_ready[stream_name].outer_html(
35
23
  selector: selector,
36
24
  html: html
37
25
  )
26
+
27
+ cable_ready.broadcast if broadcast_each
38
28
  end
39
29
 
40
30
  cable_ready.broadcast
41
31
  end
42
-
43
- private
44
-
45
- def lookup_resource(signed_params:, sgid:)
46
- return GlobalID::Locator.locate_signed(sgid) if sgid.present?
47
-
48
- message_verifier
49
- .verify(signed_params)
50
- .deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
51
- end
52
32
  end
53
33
  end
@@ -1,7 +1,6 @@
1
1
  module Futurism
2
2
  class Channel < ActionCable::Channel::Base
3
3
  include CableReady::Broadcaster
4
- include Futurism::MessageVerifier
5
4
 
6
5
  def stream_name
7
6
  ids = connection.identifiers.map { |identifier| send(identifier).try(:id) || send(identifier) }
@@ -18,19 +17,8 @@ module Futurism
18
17
  def receive(data)
19
18
  resources = data.fetch_values("signed_params", "sgids", "signed_controllers", "urls") { |_key| Array.new(data["signed_params"].length, nil) }.transpose
20
19
 
21
- resources.each do |signed_params, sgid, signed_controller, url|
22
- selector = "[data-signed-params='#{signed_params}']"
23
- selector << "[data-sgid='#{sgid}']" if sgid.present?
24
-
25
- controller = Resolver::Controller.from(signed_string: signed_controller)
26
- renderer = Resolver::Controller::Renderer.for(controller: controller,
27
- connection: connection,
28
- url: url,
29
- params: @params)
30
-
31
- resource = lookup_resource(signed_params: signed_params, sgid: sgid)
32
- html = renderer.render(resource)
33
-
20
+ resolver = Resolver::Resources.new(resource_definitions: resources, connection: connection, params: @params)
21
+ resolver.resolve do |selector, html|
34
22
  cable_ready[stream_name].outer_html(
35
23
  selector: selector,
36
24
  html: html
@@ -39,15 +27,5 @@ module Futurism
39
27
 
40
28
  cable_ready.broadcast
41
29
  end
42
-
43
- private
44
-
45
- def lookup_resource(signed_params:, sgid:)
46
- return GlobalID::Locator.locate_signed(sgid) if sgid.present?
47
-
48
- message_verifier
49
- .verify(signed_params)
50
- .deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
51
- end
52
30
  end
53
31
  end
@@ -9,48 +9,60 @@ module Futurism
9
9
  end
10
10
  end
11
11
 
12
- placeholder = capture(&block)
12
+ options[:eager] = true unless block_given?
13
13
 
14
14
  if records_or_string.is_a?(ActiveRecord::Base) || records_or_string.is_a?(ActiveRecord::Relation)
15
- futurize_active_record(records_or_string, extends: extends, placeholder: placeholder, **options)
15
+ futurize_active_record(records_or_string, extends: extends, **options, &block)
16
16
  elsif records_or_string.is_a?(String)
17
17
  html_options = options.delete(:html_options)
18
- futurize_with_options(extends: extends, placeholder: placeholder, partial: records_or_string, locals: options, html_options: html_options)
18
+ futurize_with_options(extends: extends, partial: records_or_string, locals: options, html_options: html_options, &block)
19
19
  else
20
- futurize_with_options(extends: extends, placeholder: placeholder, **options)
20
+ futurize_with_options(extends: extends, **options, &block)
21
21
  end
22
22
  end
23
23
 
24
- def futurize_with_options(extends:, placeholder:, **options)
24
+ def futurize_with_options(extends:, **options, &block)
25
25
  collection = options.delete(:collection)
26
26
  if collection.nil?
27
- Element.new(extends: extends, placeholder: placeholder, options: options).render
27
+ placeholder = capture(&block) if block_given?
28
+
29
+ WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options).render
28
30
  else
29
- collection_class_name = collection.klass.name
30
- as = options.delete(:as) || collection_class_name.downcase
31
+ collection_class_name = collection.try(:klass).try(:name) || collection.first.class.to_s
32
+ as = options.delete(:as) || collection_class_name.underscore
33
+ broadcast_each = options.delete(:broadcast_each) || false
34
+
31
35
  collection.each_with_index.map { |record, index|
32
- Element.new(extends: extends, placeholder: placeholder, options: options.deep_merge(locals: {as.to_sym => record, "#{as}_counter".to_sym => index})).render
36
+ placeholder = capture(record, index, &block) if block_given?
37
+
38
+ WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options.deep_merge(
39
+ broadcast_each: broadcast_each,
40
+ locals: {as.to_sym => record, "#{as}_counter".to_sym => index}
41
+ )).render
33
42
  }.join.html_safe
34
43
  end
35
44
  end
36
45
 
37
- def futurize_active_record(records, extends:, placeholder:, **options)
38
- Array(records).map { |record|
39
- Element.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
46
+ def futurize_active_record(records, extends:, **options, &block)
47
+ Array(records).map.with_index { |record, index|
48
+ placeholder = capture(record, index, &block) if block_given?
49
+
50
+ WrappingFuturismElement.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
40
51
  }.join.html_safe
41
52
  end
42
53
 
43
54
  # wraps functionality for rendering a futurism element
44
- class Element
55
+ class WrappingFuturismElement
45
56
  include ActionView::Helpers
46
57
  include Futurism::MessageVerifier
47
58
 
48
- attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :controller
59
+ attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :broadcast_each, :controller
49
60
 
50
61
  def initialize(extends:, placeholder:, options:)
51
62
  @extends = extends
52
63
  @placeholder = placeholder
53
64
  @eager = options.delete(:eager)
65
+ @broadcast_each = options.delete(:broadcast_each)
54
66
  @controller = options.delete(:controller)
55
67
  @html_options = options.delete(:html_options) || {}
56
68
  @data_attributes = html_options.fetch(:data, {}).except(:sgid, :signed_params)
@@ -63,6 +75,7 @@ module Futurism
63
75
  signed_params: signed_params,
64
76
  sgid: model && model.to_sgid.to_s,
65
77
  eager: eager.presence,
78
+ broadcast_each: broadcast_each.presence,
66
79
  signed_controller: signed_controller
67
80
  })
68
81
  end
@@ -82,7 +95,10 @@ module Futurism
82
95
  require_relative "shims/deep_transform_values" unless options.respond_to? :deep_transform_values
83
96
 
84
97
  options.deep_transform_values do |value|
85
- value.is_a?(ActiveRecord::Base) && !value.new_record? ? value.to_global_id.to_s : value
98
+ next(value) unless value.respond_to?(:to_global_id)
99
+ next(value) if value.is_a?(ActiveRecord::Base) && value.new_record?
100
+
101
+ value.to_global_id.to_s
86
102
  end
87
103
  end
88
104
 
@@ -9,48 +9,59 @@ module Futurism
9
9
  end
10
10
  end
11
11
 
12
- placeholder = capture(&block)
12
+ options[:eager] = true unless block_given?
13
13
 
14
14
  if records_or_string.is_a?(ActiveRecord::Base) || records_or_string.is_a?(ActiveRecord::Relation)
15
- futurize_active_record(records_or_string, extends: extends, placeholder: placeholder, **options)
15
+ futurize_active_record(records_or_string, extends: extends, **options, &block)
16
16
  elsif records_or_string.is_a?(String)
17
17
  html_options = options.delete(:html_options)
18
- futurize_with_options(extends: extends, placeholder: placeholder, partial: records_or_string, locals: options, html_options: html_options)
18
+ futurize_with_options(extends: extends, partial: records_or_string, locals: options, html_options: html_options, &block)
19
19
  else
20
- futurize_with_options(extends: extends, placeholder: placeholder, **options)
20
+ futurize_with_options(extends: extends, **options, &block)
21
21
  end
22
22
  end
23
23
 
24
- def futurize_with_options(extends:, placeholder:, **options)
24
+ def futurize_with_options(extends:, **options, &block)
25
25
  collection = options.delete(:collection)
26
26
  if collection.nil?
27
- Element.new(extends: extends, placeholder: placeholder, options: options).render
27
+ placeholder = capture(&block) if block_given?
28
+
29
+ WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options).render
28
30
  else
29
- collection_class_name = collection.klass.name
30
- as = options.delete(:as) || collection_class_name.downcase
31
- collection.map { |record|
32
- Element.new(extends: extends, placeholder: placeholder, options: options.deep_merge(locals: {as.to_sym => record})).render
31
+ placeholder = capture(record, index, &block) if block_given?
32
+
33
+ collection_class_name = collection.try(:klass).try(:name) || collection.first.class.to_s
34
+ as = options.delete(:as) || collection_class_name.underscore
35
+ broadcast_each = options.delete(:broadcast_each) || false
36
+ collection.each_with_index.map { |record, index|
37
+ WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options.deep_merge(
38
+ broadcast_each: broadcast_each,
39
+ locals: {as.to_sym => record, "#{as}_counter".to_sym => index}
40
+ )).render
33
41
  }.join.html_safe
34
42
  end
35
43
  end
36
44
 
37
- def futurize_active_record(records, extends:, placeholder:, **options)
38
- Array(records).map { |record|
39
- Element.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
45
+ def futurize_active_record(records, extends:, **options, &block)
46
+ Array(records).map.with_index { |record, index|
47
+ placeholder = capture(record, index, &block) if block_given?
48
+
49
+ WrappingFuturismElement.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
40
50
  }.join.html_safe
41
51
  end
42
52
 
43
53
  # wraps functionality for rendering a futurism element
44
- class Element
54
+ class WrappingFuturismElement
45
55
  include ActionView::Helpers
46
56
  include Futurism::MessageVerifier
47
57
 
48
- attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :controller
58
+ attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :broadcast_each, :controller
49
59
 
50
60
  def initialize(extends:, placeholder:, options:)
51
61
  @extends = extends
52
62
  @placeholder = placeholder
53
63
  @eager = options.delete(:eager)
64
+ @broadcast_each = options.delete(:broadcast_each)
54
65
  @controller = options.delete(:controller)
55
66
  @html_options = options.delete(:html_options) || {}
56
67
  @data_attributes = html_options.fetch(:data, {}).except(:sgid, :signed_params)
@@ -63,6 +74,7 @@ module Futurism
63
74
  signed_params: signed_params,
64
75
  sgid: model && model.to_sgid.to_s,
65
76
  eager: eager.presence,
77
+ broadcast_each: broadcast_each.presence,
66
78
  signed_controller: signed_controller
67
79
  })
68
80
  end
@@ -82,7 +94,10 @@ module Futurism
82
94
  require_relative "shims/deep_transform_values" unless options.respond_to? :deep_transform_values
83
95
 
84
96
  options.deep_transform_values do |value|
85
- value.is_a?(ActiveRecord::Base) && !value.new_record? ? value.to_global_id.to_s : value
97
+ next(value) unless value.respond_to?(:to_global_id)
98
+ next(value) if value.is_a?(ActiveRecord::Base) && value.new_record?
99
+
100
+ value.to_global_id.to_s
86
101
  end
87
102
  end
88
103
 
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Futurism
2
4
  module Resolver
3
5
  class Controller
4
6
  class Renderer
7
+ HTTP_METHODS = [:get, :post, :put, :patch, :delete]
8
+
5
9
  def self.for(controller:, connection:, url:, params:)
6
10
  new(controller: controller, connection: connection, url: url, params: params).renderer
7
11
  end
@@ -30,7 +34,7 @@ module Futurism
30
34
  path = ActionDispatch::Journey::Router::Utils.normalize_path(uri.path)
31
35
  query_hash = Rack::Utils.parse_nested_query(uri.query)
32
36
 
33
- path_params = Rails.application.routes.recognize_path(path)
37
+ path_params = recognize_url(url) # use full url to be more likely to match a url with subdomain constraints
34
38
 
35
39
  self.renderer =
36
40
  renderer.new(
@@ -51,6 +55,21 @@ module Futurism
51
55
  new_env = connection.env.merge(renderer.instance_variable_get(:@env))
52
56
  renderer.instance_variable_set(:@env, new_env)
53
57
  end
58
+
59
+ def recognize_url(url)
60
+ HTTP_METHODS.each do |http_method|
61
+ path = Rails.application.routes.recognize_path(url, method: http_method)
62
+ return path if path
63
+ rescue ActionController::RoutingError
64
+ # Route not matched, try next
65
+ end
66
+
67
+ warn "We were unable to find a matching rails route for '#{url}'. " \
68
+ "This may be because there are proc-based routing constraints for this particular url, or " \
69
+ "it truly is an unrecognizable url."
70
+
71
+ {}
72
+ end
54
73
  end
55
74
  end
56
75
  end
@@ -0,0 +1,101 @@
1
+ module Futurism
2
+ module Resolver
3
+ class Resources
4
+ include Futurism::MessageVerifier
5
+
6
+ # resource definitions are an array of [signed_params, sgid, signed_controller, url, broadcast_each]
7
+ def initialize(resource_definitions:, connection:, params:)
8
+ @connection = connection
9
+ @params = params
10
+ @resources_with_sgids, @resources_without_sgids = resource_definitions
11
+ .partition { |signed_params, sgid, *| sgid.present? }
12
+ .map { |partition| partition.map { |definition| ResourceDefinition.new(definition) } }
13
+ end
14
+
15
+ def resolve
16
+ resolved_models.zip(@resources_with_sgids).each do |model, resource_definition|
17
+ html = renderer_for(resource_definition: resource_definition).render(model)
18
+
19
+ yield(resource_definition.selector, html, resource_definition.broadcast_each)
20
+ end
21
+
22
+ @resources_without_sgids.each do |resource_definition|
23
+ resource = lookup_resource(resource_definition)
24
+ renderer = renderer_for(resource_definition: resource_definition)
25
+ html =
26
+ begin
27
+ renderer.render(resource)
28
+ rescue => exception
29
+ error_renderer.render(exception)
30
+ end
31
+
32
+ yield(resource_definition.selector, html, resource_definition.broadcast_each)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def error_renderer
39
+ ErrorRenderer.new
40
+ end
41
+
42
+ class ResourceDefinition
43
+ attr_reader :signed_params, :sgid, :signed_controller, :url
44
+
45
+ def initialize(resource_definition)
46
+ @signed_params, @sgid, @signed_controller, @url, @broadcast_each = resource_definition
47
+ end
48
+
49
+ def selector
50
+ selector = "[data-signed-params='#{@signed_params}']"
51
+ selector << "[data-sgid='#{@sgid}']" if @sgid.present?
52
+ selector
53
+ end
54
+
55
+ def controller
56
+ Resolver::Controller.from(signed_string: @signed_controller)
57
+ end
58
+
59
+ def broadcast_each
60
+ @broadcast_each == "true"
61
+ end
62
+ end
63
+
64
+ class ErrorRenderer
65
+ include ActionView::Helpers::TagHelper
66
+
67
+ def render(exception)
68
+ return "" unless render?
69
+
70
+ Futurism.logger.error(exception.to_s)
71
+ Futurism.logger.error(exception.backtrace)
72
+
73
+ tag.div { tag.span(exception.to_s) + tag.div(exception.backtrace.join("\n"), style: "display: none;") }
74
+ end
75
+
76
+ def render?
77
+ Rails.env.development? || Rails.env.test?
78
+ end
79
+
80
+ attr_accessor :output_buffer
81
+ end
82
+
83
+ def renderer_for(resource_definition:)
84
+ Resolver::Controller::Renderer.for(controller: resource_definition.controller,
85
+ connection: @connection,
86
+ url: resource_definition.url,
87
+ params: @params)
88
+ end
89
+
90
+ def resolved_models
91
+ GlobalID::Locator.locate_many_signed @resources_with_sgids.map(&:sgid)
92
+ end
93
+
94
+ def lookup_resource(resource_definition)
95
+ message_verifier
96
+ .verify(resource_definition.signed_params)
97
+ .deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,97 @@
1
+ module Futurism
2
+ module Resolver
3
+ class Resources
4
+ include Futurism::MessageVerifier
5
+
6
+ # resource definitions are an array of [signed_params, sgid, signed_controller, url]
7
+ def initialize(resource_definitions:, connection:, params:)
8
+ @connection = connection
9
+ @params = params
10
+ @resources_with_sgids, @resources_without_sgids = resource_definitions
11
+ .partition { |signed_params, sgid, *| sgid.present? }
12
+ .map { |partition| partition.map { |definition| ResourceDefinition.new(definition) } }
13
+ end
14
+
15
+ def resolve
16
+ resolved_models.zip(@resources_with_sgids).each do |model, resource_definition|
17
+ html = renderer_for(resource_definition: resource_definition).render(model)
18
+
19
+ yield(resource_definition.selector, html)
20
+ end
21
+
22
+ @resources_without_sgids.each do |resource_definition|
23
+ resource = lookup_resource(resource_definition)
24
+ renderer = renderer_for(resource_definition: resource_definition)
25
+ html =
26
+ begin
27
+ renderer.render(resource)
28
+ rescue => exception
29
+ error_renderer.render(exception)
30
+ end
31
+
32
+ yield(resource_definition.selector, html)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def error_renderer
39
+ ErrorRenderer.new
40
+ end
41
+
42
+ class ResourceDefinition
43
+ attr_reader :signed_params, :sgid, :signed_controller, :url
44
+
45
+ def initialize(resource_definition)
46
+ @signed_params, @sgid, @signed_controller, @url = resource_definition
47
+ end
48
+
49
+ def selector
50
+ selector = "[data-signed-params='#{@signed_params}']"
51
+ selector << "[data-sgid='#{@sgid}']" if @sgid.present?
52
+ selector
53
+ end
54
+
55
+ def controller
56
+ Resolver::Controller.from(signed_string: @signed_controller)
57
+ end
58
+ end
59
+
60
+ class ErrorRenderer
61
+ include ActionView::Helpers::TagHelper
62
+
63
+ def render(exception)
64
+ return "" unless render?
65
+
66
+ Futurism.logger.error(exception.to_s)
67
+ Futurism.logger.error(exception.backtrace)
68
+
69
+ tag.div { tag.span(exception.to_s) + tag.div(exception.backtrace.join("\n"), style: "display: none;") }
70
+ end
71
+
72
+ def render?
73
+ Rails.env.development? || Rails.env.test?
74
+ end
75
+
76
+ attr_accessor :output_buffer
77
+ end
78
+
79
+ def renderer_for(resource_definition:)
80
+ Resolver::Controller::Renderer.for(controller: resource_definition.controller,
81
+ connection: @connection,
82
+ url: resource_definition.url,
83
+ params: @params)
84
+ end
85
+
86
+ def resolved_models
87
+ GlobalID::Locator.locate_many_signed @resources_with_sgids.map(&:sgid)
88
+ end
89
+
90
+ def lookup_resource(resource_definition)
91
+ message_verifier
92
+ .verify(resource_definition.signed_params)
93
+ .deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,3 +1,3 @@
1
1
  module Futurism
2
- VERSION = "0.7.2"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,3 @@
1
+ module Futurism
2
+ VERSION = "1.0.0"
3
+ end
data/lib/futurism.rb CHANGED
@@ -3,6 +3,7 @@ require "action_cable"
3
3
  require "cable_ready"
4
4
  require "futurism/engine"
5
5
  require "futurism/message_verifier"
6
+ require "futurism/resolver/resources"
6
7
  require "futurism/resolver/controller"
7
8
  require "futurism/resolver/controller/renderer"
8
9
  require "futurism/channel"
@@ -20,7 +21,10 @@ module Futurism
20
21
  (@@default_controller || "::ApplicationController").to_s.constantize
21
22
  end
22
23
 
23
- ActiveSupport.on_load(:action_view) {
24
+ ActiveSupport.on_load(:action_view) do
24
25
  include Futurism::Helpers
25
- }
26
+ end
27
+
28
+ mattr_accessor :logger
29
+ self.logger ||= Rails.logger ? Rails.logger.new : Logger.new($stdout)
26
30
  end
@@ -3,7 +3,7 @@ require "fileutils"
3
3
  namespace :futurism do
4
4
  desc "Let's look into a brighter future with futurism and CableReady"
5
5
  task install: :environment do
6
- system "yarn add @minthesize/futurism"
6
+ system "yarn add @stimulus_reflex/futurism"
7
7
 
8
8
  filepath = %w[
9
9
  app/javascript/channels/index.js
@@ -20,7 +20,7 @@ namespace :futurism do
20
20
 
21
21
  unless lines.find { |line| line.start_with?("import * as Futurism") }
22
22
  matches = lines.select { |line| line =~ /\A(require|import)/ }
23
- lines.insert lines.index(matches.last).to_i + 1, "import * as Futurism from '@minthesize/futurism'\n"
23
+ lines.insert lines.index(matches.last).to_i + 1, "import * as Futurism from '@stimulus_reflex/futurism'\n"
24
24
  end
25
25
 
26
26
  unless lines.find { |line| line.start_with?("import consumer") }
@@ -1,4 +1,39 @@
1
- # desc "Explaining what the task does"
2
- # task :futurism do
3
- # # Task goes here
4
- # end
1
+ require "fileutils"
2
+
3
+ namespace :futurism do
4
+ desc "Let's look into a brighter future with futurism and CableReady"
5
+ task install: :environment do
6
+ system "yarn add @minthesize/futurism"
7
+
8
+ filepath = %w[
9
+ app/javascript/channels/index.js
10
+ app/javascript/channels/index.ts
11
+ app/javascript/packs/application.js
12
+ app/javascript/packs/application.ts
13
+ ]
14
+ .select { |path| File.exist?(path) }
15
+ .map { |path| Rails.root.join(path) }
16
+ .first
17
+
18
+ puts "Updating #{filepath}"
19
+ lines = File.open(filepath, "r") { |f| f.readlines }
20
+
21
+ unless lines.find { |line| line.start_with?("import * as Futurism") }
22
+ matches = lines.select { |line| line =~ /\A(require|import)/ }
23
+ lines.insert lines.index(matches.last).to_i + 1, "import * as Futurism from '@minthesize/futurism'\n"
24
+ end
25
+
26
+ unless lines.find { |line| line.start_with?("import consumer") }
27
+ matches = lines.select { |line| line =~ /\A(require|import)/ }
28
+ lines.insert lines.index(matches.last).to_i + 1, "import consumer from '../channels/consumer'\n"
29
+ end
30
+
31
+ initialize_line = lines.find { |line| line.start_with?("Futurism.initializeElements") }
32
+ lines << "Futurism.initializeElements()\n" unless initialize_line
33
+
34
+ subscribe_line = lines.find { |line| line.start_with?("Futurism.createSubscription") }
35
+ lines << "Futurism.createSubscription(consumer)\n" unless subscribe_line
36
+
37
+ File.open(filepath, "w") { |f| f.write lines.join }
38
+ end
39
+ end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: futurism
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julian Rubisch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-07 00:00:00.000000000 Z
11
+ date: 2021-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: appraisal
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: bundler
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -174,11 +160,14 @@ files:
174
160
  - lib/futurism/resolver/controller.rb~
175
161
  - lib/futurism/resolver/controller/renderer.rb
176
162
  - lib/futurism/resolver/controller/renderer.rb~
163
+ - lib/futurism/resolver/resources.rb
164
+ - lib/futurism/resolver/resources.rb~
177
165
  - lib/futurism/shims/deep_transform_values.rb
178
166
  - lib/futurism/version.rb
167
+ - lib/futurism/version.rb~
179
168
  - lib/tasks/futurism_tasks.rake
180
169
  - lib/tasks/futurism_tasks.rake~
181
- homepage: https://github.com/julianrubisch/futurism
170
+ homepage: https://github.com/stimulusreflex/futurism
182
171
  licenses:
183
172
  - MIT
184
173
  metadata: {}