futurism 0.7.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: {}