futurism 0.8.0 → 1.0.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: 75dd023300659792f3dd9e114727b0003204ed6b2e9e990413fcc1ca82971fba
4
- data.tar.gz: b73280cb140625612fc5e7188e301f98e5be0eb19e7359c7786fae8b4a8f4b8c
3
+ metadata.gz: 414f6857d0112042c7745a9198e9e9c05d2886d3649f955d53d4ed09fed1dddc
4
+ data.tar.gz: cd55e6f01214c0379e2a5b2d0505c76486cc388114daa3888439e608a0a74335
5
5
  SHA512:
6
- metadata.gz: c9285da73bb00137f7aea08b2171d370b032681abd79b8d37e652b1aef546159a2d568ecc695ecaeb0a7f3a8f4a74abb0a6a310dca9969021923d4590c0fd03b
7
- data.tar.gz: ae425d91dcae19d847105b5633a0f0b434166d5cb45f9aed14bfccb2a07cc9661594c2a814d072b541b30d7b401c6273919e6c199ffcdb38c146d6fc8f8a6a74
6
+ metadata.gz: 14de66605b2c41ef1cc6aca2c42507a856dfd67cbb1dc23de029b77bfb5c24b5b6a5a2ecf65b95cef5622c45dfef6ecccd05059daeaf25615a12356015b5b614
7
+ data.tar.gz: a63476d5c42a5db0ae0220f71b4c5a4792aaf529c0741df322a239c8be458b8433c94bd461574833c1b102e1b8f444038adb1c7f66a3a16d6b73025672fa8022
data/README.md CHANGED
@@ -5,7 +5,7 @@
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,7 @@ 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)
24
25
  - [Events](#events)
25
26
  - [Installation](#installation)
26
27
  - [Manual Installation](#manual-installation)
@@ -33,7 +34,7 @@ Lazy-load Rails partials via CableReady
33
34
 
34
35
  ## Facts
35
36
  - only one dependency: CableReady
36
- - bundle size (without CableReady) is around [~2.46kB](https://bundlephobia.com/result?p=@minthesize/futurism@0.7.2)
37
+ - bundle size (without CableReady) is around [~2.46kB](https://bundlephobia.com/result?p=@stimulus_reflex/futurism@0.7.2)
37
38
 
38
39
  ### Browser Support
39
40
 
@@ -68,6 +69,8 @@ You can pass the placeholder as a block:
68
69
 
69
70
  ![aa601dec1930151f71dbf0d6b02b61c9](https://user-images.githubusercontent.com/4352208/87131629-f768a480-c294-11ea-89a9-ea0a76ee06ef.gif)
70
71
 
72
+ You can also omit the placeholder, which falls back to [eager loading](#eager-loading).
73
+
71
74
  ## API
72
75
 
73
76
  Currently there are two ways to call `futurize`, designed to wrap `render`'s behavior:
@@ -171,6 +174,19 @@ Futurism makes that dead simple:
171
174
  <% end %>
172
175
  ```
173
176
 
177
+ ### Broadcast Partials Individually
178
+ Futurism's default behavior is to `broadcast` partials as they are generated in batches:
179
+
180
+ 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).
181
+
182
+ For collections, however, you can opt into individual broadcasts by specifying `broadcast_each: true` in your helper usage:
183
+
184
+ ```erb
185
+ <%= futurize @posts, broadcast_each: true, extends: :tr do %>
186
+ <div class="placeholder"</td>
187
+ <% end %>
188
+ ```
189
+
174
190
  ## Events
175
191
 
176
192
  Once your futurize element has been rendered, the `futurize:appeared` custom event will be called.
@@ -193,19 +209,19 @@ To copy over the javascript files to your application, run
193
209
  $ bin/rails futurism:install
194
210
  ```
195
211
 
196
- **! Note that the installer will run `yarn add @minthesize/futurism` for you !**
212
+ **! Note that the installer will run `yarn add @stimulus_reflex/futurism` for you !**
197
213
 
198
214
  ### Manual Installation
199
215
  After `bundle`, install the Javascript library:
200
216
 
201
217
  ```bash
202
- $ bin/yarn add @minthesize/futurism
218
+ $ bin/yarn add @stimulus_reflex/futurism
203
219
  ```
204
220
 
205
221
  In your `app/javascript/channels/index.js`, add the following
206
222
 
207
223
  ```js
208
- import * as Futurism from '@minthesize/futurism'
224
+ import * as Futurism from '@stimulus_reflex/futurism'
209
225
 
210
226
  import consumer from './consumer'
211
227
 
@@ -273,7 +289,7 @@ git clone https://github.com/leastbad/stimulus_reflex_harness.git
273
289
  cd stimulus_reflex_harness
274
290
  git checkout futurism
275
291
  # Edit Gemfile to point point to local gem (e.g. `gem "futurism", path: "../futurism"`)
276
- # yarn link @minthesize/futurism
292
+ # yarn link @stimulus_reflex/futurism
277
293
 
278
294
 
279
295
  # Do your work, Submit PR, Profit!
@@ -290,6 +306,12 @@ cd path/to/project
290
306
  yarn install --force
291
307
  ```
292
308
 
309
+ ### Release
310
+
311
+ 1. Update the version numbers in `javascript/package.json` and `lib/futurism/version.rb`
312
+ 2. `rake release`
313
+ 3. `cd javascript && npm publish --access public`
314
+
293
315
  ## License
294
316
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
295
317
 
@@ -302,25 +324,25 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
302
324
  <!-- markdownlint-disable -->
303
325
  <table>
304
326
  <tr>
305
- <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/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?s=100" 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?s=100" 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>
327
+ <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>
328
+ <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>
329
+ <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>
308
330
  <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>
309
- <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/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?s=100" 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?s=100" 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>
331
+ <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>
332
+ <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>
333
+ <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
334
  </tr>
313
335
  <tr>
314
- <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/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?s=100" 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?s=100" 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?s=100" 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?s=100" 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?s=100" 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?s=100" 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>
336
+ <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>
337
+ <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>
338
+ <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>
339
+ <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>
340
+ <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>
341
+ <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>
342
+ <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>
321
343
  </tr>
322
344
  <tr>
323
- <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/julianrubisch/futurism/commits?author=mansakondo" title="Code">💻</a></td>
345
+ <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>
324
346
  </tr>
325
347
  </table>
326
348
 
@@ -0,0 +1,105 @@
1
+ module Futurism
2
+ module Helpers
3
+ def futurize(records_or_string = nil, extends:, **options, &block)
4
+ if Rails.env.test? && Futurism.skip_in_test
5
+ if records_or_string.nil?
6
+ return render(**options)
7
+ else
8
+ return render(records_or_string, **options)
9
+ end
10
+ end
11
+
12
+ placeholder = capture(&block)
13
+
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)
16
+ elsif records_or_string.is_a?(String)
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)
19
+ else
20
+ futurize_with_options(extends: extends, placeholder: placeholder, **options)
21
+ end
22
+ end
23
+
24
+ def futurize_with_options(extends:, placeholder:, **options)
25
+ collection = options.delete(:collection)
26
+ if collection.nil?
27
+ WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options).render
28
+ else
29
+ collection_class_name = collection.try(:klass).try(:name) || collection.first.class.to_s
30
+ as = options.delete(:as) || collection_class_name.underscore
31
+ collection.each_with_index.map { |record, index|
32
+ WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options.deep_merge(locals: {as.to_sym => record, "#{as}_counter".to_sym => index})).render
33
+ }.join.html_safe
34
+ end
35
+ end
36
+
37
+ def futurize_active_record(records, extends:, placeholder:, **options)
38
+ Array(records).map { |record|
39
+ WrappingFuturismElement.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
40
+ }.join.html_safe
41
+ end
42
+
43
+ # wraps functionality for rendering a futurism element
44
+ class WrappingFuturismElement
45
+ include ActionView::Helpers
46
+ include Futurism::MessageVerifier
47
+
48
+ attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :controller
49
+
50
+ def initialize(extends:, placeholder:, options:)
51
+ @extends = extends
52
+ @placeholder = placeholder
53
+ @eager = options.delete(:eager)
54
+ @controller = options.delete(:controller)
55
+ @html_options = options.delete(:html_options) || {}
56
+ @data_attributes = html_options.fetch(:data, {}).except(:sgid, :signed_params)
57
+ @model = options.delete(:model)
58
+ @options = data_attributes.any? ? options.merge(data: data_attributes) : options
59
+ end
60
+
61
+ def dataset
62
+ data_attributes.merge({
63
+ signed_params: signed_params,
64
+ sgid: model && model.to_sgid.to_s,
65
+ eager: eager.presence,
66
+ signed_controller: signed_controller
67
+ })
68
+ end
69
+
70
+ def render
71
+ case extends
72
+ when :li
73
+ content_tag :li, placeholder, html_options.deep_merge({data: dataset, is: "futurism-li"})
74
+ when :tr
75
+ content_tag :tr, placeholder, html_options.deep_merge({data: dataset, is: "futurism-table-row"})
76
+ else
77
+ content_tag :"futurism-element", placeholder, html_options.deep_merge({data: dataset})
78
+ end
79
+ end
80
+
81
+ def transformed_options
82
+ require_relative "shims/deep_transform_values" unless options.respond_to? :deep_transform_values
83
+
84
+ options.deep_transform_values do |value|
85
+ next(value) unless value.respond_to?(:to_global_id)
86
+ next(value) if value.is_a?(ActiveRecord::Base) && value.new_record?
87
+
88
+ value.to_global_id.to_s
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def signed_params
95
+ message_verifier.generate(transformed_options)
96
+ end
97
+
98
+ def signed_controller
99
+ return unless controller.present?
100
+
101
+ message_verifier.generate(controller.to_s)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -15,14 +15,16 @@ module Futurism
15
15
  end
16
16
 
17
17
  def receive(data)
18
- resources = data.fetch_values("signed_params", "sgids", "signed_controllers", "urls") { |_key| Array.new(data["signed_params"].length, nil) }.transpose
18
+ resources = data.fetch_values("signed_params", "sgids", "signed_controllers", "urls", "broadcast_each") { |_key| Array.new(data["signed_params"].length, nil) }.transpose
19
19
 
20
20
  resolver = Resolver::Resources.new(resource_definitions: resources, connection: connection, params: @params)
21
- resolver.resolve do |selector, html|
21
+ resolver.resolve do |selector, html, broadcast_each|
22
22
  cable_ready[stream_name].outer_html(
23
23
  selector: selector,
24
24
  html: html
25
25
  )
26
+
27
+ cable_ready.broadcast if broadcast_each
26
28
  end
27
29
 
28
30
  cable_ready.broadcast
@@ -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,23 +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_with_sgids, resources_without_sgids = resources.partition { |signed_params, sgid, *| sgid.present? }
22
-
23
- GlobalID::Locator.locate_many_signed resources_with_sgids.map(&:second)
24
-
25
- resources_without_sgids.each do |signed_params, sgid, signed_controller, url|
26
- selector = "[data-signed-params='#{signed_params}']"
27
- selector << "[data-sgid='#{sgid}']" if sgid.present?
28
-
29
- controller = Resolver::Controller.from(signed_string: signed_controller)
30
- renderer = Resolver::Controller::Renderer.for(controller: controller,
31
- connection: connection,
32
- url: url,
33
- params: @params)
34
-
35
- resource = lookup_resource(signed_params: signed_params, sgid: sgid)
36
- html = renderer.render(resource)
37
-
20
+ resolver = Resolver::Resources.new(resource_definitions: resources, connection: connection, params: @params)
21
+ resolver.resolve do |selector, html|
38
22
  cable_ready[stream_name].outer_html(
39
23
  selector: selector,
40
24
  html: html
@@ -43,15 +27,5 @@ module Futurism
43
27
 
44
28
  cable_ready.broadcast
45
29
  end
46
-
47
- private
48
-
49
- def lookup_resource(signed_params:, sgid:)
50
- return GlobalID::Locator.locate_signed(sgid) if sgid.present?
51
-
52
- message_verifier
53
- .verify(signed_params)
54
- .deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
55
- end
56
30
  end
57
31
  end
@@ -9,7 +9,11 @@ module Futurism
9
9
  end
10
10
  end
11
11
 
12
- placeholder = capture(&block)
12
+ if block_given?
13
+ placeholder = capture(&block)
14
+ else
15
+ options[:eager] = true
16
+ end
13
17
 
14
18
  if records_or_string.is_a?(ActiveRecord::Base) || records_or_string.is_a?(ActiveRecord::Relation)
15
19
  futurize_active_record(records_or_string, extends: extends, placeholder: placeholder, **options)
@@ -24,33 +28,38 @@ module Futurism
24
28
  def futurize_with_options(extends:, placeholder:, **options)
25
29
  collection = options.delete(:collection)
26
30
  if collection.nil?
27
- Element.new(extends: extends, placeholder: placeholder, options: options).render
31
+ WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options).render
28
32
  else
29
33
  collection_class_name = collection.try(:klass).try(:name) || collection.first.class.to_s
30
- as = options.delete(:as) || collection_class_name.downcase
34
+ as = options.delete(:as) || collection_class_name.underscore
35
+ broadcast_each = options.delete(:broadcast_each) || false
31
36
  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
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
45
  def futurize_active_record(records, extends:, placeholder:, **options)
38
46
  Array(records).map { |record|
39
- Element.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
47
+ WrappingFuturismElement.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
40
48
  }.join.html_safe
41
49
  end
42
50
 
43
51
  # wraps functionality for rendering a futurism element
44
- class Element
52
+ class WrappingFuturismElement
45
53
  include ActionView::Helpers
46
54
  include Futurism::MessageVerifier
47
55
 
48
- attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :controller
56
+ attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :broadcast_each, :controller
49
57
 
50
58
  def initialize(extends:, placeholder:, options:)
51
59
  @extends = extends
52
60
  @placeholder = placeholder
53
61
  @eager = options.delete(:eager)
62
+ @broadcast_each = options.delete(:broadcast_each)
54
63
  @controller = options.delete(:controller)
55
64
  @html_options = options.delete(:html_options) || {}
56
65
  @data_attributes = html_options.fetch(:data, {}).except(:sgid, :signed_params)
@@ -63,6 +72,7 @@ module Futurism
63
72
  signed_params: signed_params,
64
73
  sgid: model && model.to_sgid.to_s,
65
74
  eager: eager.presence,
75
+ broadcast_each: broadcast_each.presence,
66
76
  signed_controller: signed_controller
67
77
  })
68
78
  end
@@ -82,7 +92,10 @@ module Futurism
82
92
  require_relative "shims/deep_transform_values" unless options.respond_to? :deep_transform_values
83
93
 
84
94
  options.deep_transform_values do |value|
85
- value.is_a?(ActiveRecord::Base) && !value.new_record? ? value.to_global_id.to_s : value
95
+ next(value) unless value.respond_to?(:to_global_id)
96
+ next(value) if value.is_a?(ActiveRecord::Base) && value.new_record?
97
+
98
+ value.to_global_id.to_s
86
99
  end
87
100
  end
88
101
 
@@ -9,7 +9,11 @@ module Futurism
9
9
  end
10
10
  end
11
11
 
12
- placeholder = capture(&block)
12
+ if block_given?
13
+ placeholder = capture(&block)
14
+ else
15
+ options[:eager] = true
16
+ end
13
17
 
14
18
  if records_or_string.is_a?(ActiveRecord::Base) || records_or_string.is_a?(ActiveRecord::Relation)
15
19
  futurize_active_record(records_or_string, extends: extends, placeholder: placeholder, **options)
@@ -24,24 +28,28 @@ module Futurism
24
28
  def futurize_with_options(extends:, placeholder:, **options)
25
29
  collection = options.delete(:collection)
26
30
  if collection.nil?
27
- Element.new(extends: extends, placeholder: placeholder, options: options).render
31
+ WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options).render
28
32
  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
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
45
  def futurize_active_record(records, extends:, placeholder:, **options)
38
46
  Array(records).map { |record|
39
- Element.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
47
+ WrappingFuturismElement.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
40
48
  }.join.html_safe
41
49
  end
42
50
 
43
51
  # wraps functionality for rendering a futurism element
44
- class Element
52
+ class WrappingFuturismElement
45
53
  include ActionView::Helpers
46
54
  include Futurism::MessageVerifier
47
55
 
@@ -82,7 +90,10 @@ module Futurism
82
90
  require_relative "shims/deep_transform_values" unless options.respond_to? :deep_transform_values
83
91
 
84
92
  options.deep_transform_values do |value|
85
- value.is_a?(ActiveRecord::Base) && !value.new_record? ? value.to_global_id.to_s : value
93
+ next(value) unless value.respond_to?(:to_global_id)
94
+ next(value) if value.is_a?(ActiveRecord::Base) && value.new_record?
95
+
96
+ value.to_global_id.to_s
86
97
  end
87
98
  end
88
99
 
@@ -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
@@ -3,7 +3,7 @@ module Futurism
3
3
  class Resources
4
4
  include Futurism::MessageVerifier
5
5
 
6
- # resource definitions are an array of [signed_params, sgid, signed_controller, url]
6
+ # resource definitions are an array of [signed_params, sgid, signed_controller, url, broadcast_each]
7
7
  def initialize(resource_definitions:, connection:, params:)
8
8
  @connection = connection
9
9
  @params = params
@@ -16,24 +16,34 @@ module Futurism
16
16
  resolved_models.zip(@resources_with_sgids).each do |model, resource_definition|
17
17
  html = renderer_for(resource_definition: resource_definition).render(model)
18
18
 
19
- yield(resource_definition.selector, html)
19
+ yield(resource_definition.selector, html, resource_definition.broadcast_each)
20
20
  end
21
21
 
22
22
  @resources_without_sgids.each do |resource_definition|
23
23
  resource = lookup_resource(resource_definition)
24
- html = renderer_for(resource_definition: resource_definition).render(resource)
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
25
31
 
26
- yield(resource_definition.selector, html)
32
+ yield(resource_definition.selector, html, resource_definition.broadcast_each)
27
33
  end
28
34
  end
29
35
 
30
36
  private
31
37
 
38
+ def error_renderer
39
+ ErrorRenderer.new
40
+ end
41
+
32
42
  class ResourceDefinition
33
43
  attr_reader :signed_params, :sgid, :signed_controller, :url
34
44
 
35
45
  def initialize(resource_definition)
36
- @signed_params, @sgid, @signed_controller, @url = resource_definition
46
+ @signed_params, @sgid, @signed_controller, @url, @broadcast_each = resource_definition
37
47
  end
38
48
 
39
49
  def selector
@@ -45,6 +55,29 @@ module Futurism
45
55
  def controller
46
56
  Resolver::Controller.from(signed_string: @signed_controller)
47
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
48
81
  end
49
82
 
50
83
  def renderer_for(resource_definition:)
@@ -9,65 +9,89 @@ module Futurism
9
9
  @params = params
10
10
  @resources_with_sgids, @resources_without_sgids = resource_definitions
11
11
  .partition { |signed_params, sgid, *| sgid.present? }
12
- .map { |definition| ResourceDefinition.new(definition) }
12
+ .map { |partition| partition.map { |definition| ResourceDefinition.new(definition) } }
13
13
  end
14
14
 
15
15
  def resolve
16
16
  resolved_models.zip(@resources_with_sgids).each do |model, resource_definition|
17
- signed_params, sgid, signed_controller, url = resource_definition
17
+ html = renderer_for(resource_definition: resource_definition).render(model)
18
18
 
19
- selector = selector_for(signed_params: signed_params, sgid: sgid)
20
-
21
- controller = Resolver::Controller.from(signed_string: signed_controller)
22
- renderer = Resolver::Controller::Renderer.for(controller: controller,
23
- connection: @connection,
24
- url: url,
25
- params: @params)
26
-
27
- html = renderer.render(model)
28
-
29
- yield(selector, html)
19
+ yield(resource_definition.selector, html)
30
20
  end
31
21
 
32
- @resources_without_sgids.each do |signed_params, sgid, signed_controller, url|
33
- selector = selector_for(signed_params: signed_params, sgid: sgid)
34
-
35
- controller = Resolver::Controller.from(signed_string: signed_controller)
36
- renderer = Resolver::Controller::Renderer.for(controller: controller,
37
- connection: @connection,
38
- url: url,
39
- params: @params)
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
40
31
 
41
- resource = lookup_resource(signed_params: signed_params)
42
- html = renderer.render(resource)
43
-
44
- yield(selector, html)
32
+ yield(resource_definition.selector, html)
45
33
  end
46
34
  end
47
35
 
48
36
  private
49
37
 
38
+ def error_renderer
39
+ ErrorRenderer.new
40
+ end
41
+
50
42
  class ResourceDefinition
43
+ attr_reader :signed_params, :sgid, :signed_controller, :url
44
+
51
45
  def initialize(resource_definition)
52
46
  @signed_params, @sgid, @signed_controller, @url = resource_definition
53
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)
54
84
  end
55
85
 
56
86
  def resolved_models
57
- GlobalID::Locator.locate_many_signed @resources_with_sgids.map(&:second)
87
+ GlobalID::Locator.locate_many_signed @resources_with_sgids.map(&:sgid)
58
88
  end
59
89
 
60
- def lookup_resource(signed_params:)
90
+ def lookup_resource(resource_definition)
61
91
  message_verifier
62
- .verify(signed_params)
92
+ .verify(resource_definition.signed_params)
63
93
  .deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
64
94
  end
65
-
66
- def selector_for(signed_params:, sgid:)
67
- selector = "[data-signed-params='#{signed_params}']"
68
- selector << "[data-sgid='#{sgid}']" if sgid.present?
69
- selector
70
- end
71
95
  end
72
96
  end
73
97
  end
@@ -1,3 +1,3 @@
1
1
  module Futurism
2
- VERSION = "0.8.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,3 +1,3 @@
1
1
  module Futurism
2
- VERSION = "0.7.2"
2
+ VERSION = "0.8.0"
3
3
  end
data/lib/futurism.rb CHANGED
@@ -21,7 +21,10 @@ module Futurism
21
21
  (@@default_controller || "::ApplicationController").to_s.constantize
22
22
  end
23
23
 
24
- ActiveSupport.on_load(:action_view) {
24
+ ActiveSupport.on_load(:action_view) do
25
25
  include Futurism::Helpers
26
- }
26
+ end
27
+
28
+ mattr_accessor :logger
29
+ self.logger ||= Rails.logger ? Rails.logger.new : Logger.new($stdout)
27
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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: futurism
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 1.0.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: 2021-04-23 00:00:00.000000000 Z
11
+ date: 2021-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -150,6 +150,7 @@ files:
150
150
  - config/routes.rb
151
151
  - lib/futurism.rb
152
152
  - lib/futurism.rb~
153
+ - lib/futurism/#helpers.rb#
153
154
  - lib/futurism/channel.rb
154
155
  - lib/futurism/channel.rb~
155
156
  - lib/futurism/engine.rb
@@ -167,7 +168,7 @@ files:
167
168
  - lib/futurism/version.rb~
168
169
  - lib/tasks/futurism_tasks.rake
169
170
  - lib/tasks/futurism_tasks.rake~
170
- homepage: https://github.com/julianrubisch/futurism
171
+ homepage: https://github.com/stimulusreflex/futurism
171
172
  licenses:
172
173
  - MIT
173
174
  metadata: {}