futurism 0.5.2 → 0.7.2

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: 313e2de708762f264a6b0b647e9ae7e1eff5f2463e4ba852e95696ffd8706fb3
4
- data.tar.gz: 73f61a471267578dba1e2460cb0c388d96bdf46a52bcae5a047e9c729fe76095
3
+ metadata.gz: 010431b087816c5ad0289d895d9206e6fc88382fa1f291fc4deb36d2c2910b42
4
+ data.tar.gz: ea1d6fd1574cabc5e91dc0d81dba80d0f64d05e9efdaf928614a457f2cc4b1d5
5
5
  SHA512:
6
- metadata.gz: f205ad83640359aaaf72414069bd8a287faf5177b8ec320298c39b82db75eedab9799c45cbff003478804f432d033fc6fe65522ca1de7a5932601fe387c7e8a2
7
- data.tar.gz: 56b67210ed5b229298e07216d5b920b358c43758d825ce6dc906faf4a2289dc20feafb2aa8e25ff846802c8ca6bc8fce6061c20f210b1a37690d5798349cac7d
6
+ metadata.gz: 399ad5f2f189e569eeb031b3976137d5f9d66ee1a46c0c70ec5dd1e421817c99d525755bb16dd73146d49c15392cd2e6ea75e9f2fe44f82878b7a56e996856f8
7
+ data.tar.gz: bcd8cf6da3d6ce6557dee1d1dcb429beb49642bfaa9753a24d122265e43350022cf5bdd48b259732b4a91a7bb18d266feaa9a2819c800f8709f84eae5c9b86f9
data/README.md CHANGED
@@ -25,6 +25,7 @@ Lazy-load Rails partials via CableReady
25
25
  - [Installation](#installation)
26
26
  - [Manual Installation](#manual-installation)
27
27
  - [Authentication](#authentication)
28
+ - [Testing](#testing)
28
29
  - [Gotchas](#gotchas)
29
30
  - [Contributing](#contributing)
30
31
  - [License](#license)
@@ -123,6 +124,22 @@ Collection rendering is also possible:
123
124
  <% end %>
124
125
  ```
125
126
 
127
+ #### Specifying Controller to Render
128
+
129
+ You can also pass in the controller that will be used to render the partial.
130
+
131
+ ```erb
132
+ <%= futurize partial: "items/card", collection: @cards, controller: MyController, extends: :div do %>
133
+ <div class="spinner"></div>
134
+ <% end %>
135
+ ```
136
+
137
+ By default (i.e. not passing in a value), futurize will use `ApplicationController`, but you may override by setting the Futurism default controller in an initializer, for example `config/initializers/futurism.rb`.
138
+
139
+ ```ruby
140
+ Futurism.default_controller = "MyController" # to avoid the controller from trying to autoload at boot, provide as a string
141
+ ```
142
+
126
143
  ### HTML Options
127
144
 
128
145
  You can pass a hash of attribute/value pairs which will be mixed into the HTML markup for the placeholder element. This is important for layouts that require elements to have dimensionality. For example, many scripts calculate size based on element height and width. This option ensures that your elements have integrity, even if they are gone before you see them.
@@ -213,6 +230,15 @@ end
213
230
 
214
231
  The [Stimulus Reflex Docs](https://docs.stimulusreflex.com/authentication) have an excellent section about all sorts of authentication.
215
232
 
233
+ ## Testing
234
+ In Rails system tests there is a chance that flaky errors will occur due to Capybara not waiting for the placeholder elements to be replaced. To overcome this, add the flag
235
+
236
+ ```ruby
237
+ Futurism.skip_in_test = true
238
+ ```
239
+
240
+ to an initializer, for example `config/initializers/futurism.rb`.
241
+
216
242
  ## Gotchas
217
243
 
218
244
  ### ActiveStorage URLs aren't correct in development
@@ -231,6 +257,39 @@ to your environments.
231
257
 
232
258
  ## Contributing
233
259
 
260
+ ### Get local environment setup
261
+
262
+ Below are a set of instructions that may help you get a local development environment working
263
+
264
+ ```shell
265
+ # Get the gem/npm package source locally
266
+ git clone futurism
267
+ cd futurism/javascript
268
+ yarn install # install all of the npm package's dependencies
269
+ yarn link # set the local machine's futurism npm package's lookup to this local path
270
+
271
+ # Setup a sample project, use the information below directly or use your own project
272
+ git clone https://github.com/leastbad/stimulus_reflex_harness.git
273
+ cd stimulus_reflex_harness
274
+ git checkout futurism
275
+ # Edit Gemfile to point point to local gem (e.g. `gem "futurism", path: "../futurism"`)
276
+ # yarn link @minthesize/futurism
277
+
278
+
279
+ # Do your work, Submit PR, Profit!
280
+
281
+
282
+ # To stop using your local version of futurism
283
+ # change your Gemfile back to the published (e.g. `gem "futurism"`)
284
+ cd path/to/futurism/javascript
285
+ # Stop using the local npm package
286
+ yarn unlink
287
+
288
+ # Instruct your project to reinstall the published version of the npm package
289
+ cd path/to/project
290
+ yarn install --force
291
+ ```
292
+
234
293
  ## License
235
294
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
236
295
 
@@ -2,6 +2,9 @@ require "rails"
2
2
  require "action_cable"
3
3
  require "cable_ready"
4
4
  require "futurism/engine"
5
+ require "futurism/message_verifier"
6
+ require "futurism/resolver/controller"
7
+ require "futurism/resolver/controller/renderer"
5
8
  require "futurism/channel"
6
9
  require "futurism/helpers"
7
10
 
@@ -10,6 +13,13 @@ module Futurism
10
13
 
11
14
  autoload :Helpers, "futurism/helpers"
12
15
 
16
+ mattr_accessor :skip_in_test, default: false
17
+
18
+ mattr_writer :default_controller
19
+ def self.default_controller
20
+ (@@default_controller || "::ApplicationController").to_s.constantize
21
+ end
22
+
13
23
  ActiveSupport.on_load(:action_view) {
14
24
  include Futurism::Helpers
15
25
  }
@@ -1,5 +1,26 @@
1
+ require "rails"
2
+ require "action_cable"
3
+ require "cable_ready"
1
4
  require "futurism/engine"
5
+ require "futurism/message_verifier"
6
+ require "futurism/resolver/controller"
7
+ require "futurism/resolver/controller/renderer"
8
+ require "futurism/channel"
9
+ require "futurism/helpers"
2
10
 
3
11
  module Futurism
4
- # Your code goes here...
12
+ extend ActiveSupport::Autoload
13
+
14
+ autoload :Helpers, "futurism/helpers"
15
+
16
+ mattr_accessor :skip_in_test, default: false
17
+
18
+ mattr_writer :default_controller
19
+ def self.default_controller
20
+ (@@default_controller || "::ApplicationController").to_s.constantize
21
+ end
22
+
23
+ ActiveSupport.on_load(:action_view) {
24
+ include Futurism::Helpers
25
+ }
5
26
  end
@@ -1,6 +1,7 @@
1
1
  module Futurism
2
2
  class Channel < ActionCable::Channel::Base
3
3
  include CableReady::Broadcaster
4
+ include Futurism::MessageVerifier
4
5
 
5
6
  def stream_name
6
7
  ids = connection.identifiers.map { |identifier| send(identifier).try(:id) || send(identifier) }
@@ -15,17 +16,24 @@ module Futurism
15
16
  end
16
17
 
17
18
  def receive(data)
18
- resources = data.fetch_values("signed_params", "sgids") { |key| [nil] }.transpose
19
+ resources = data.fetch_values("signed_params", "sgids", "signed_controllers", "urls") { |_key| Array.new(data["signed_params"].length, nil) }.transpose
19
20
 
20
- new_env = connection.env.merge(ApplicationController.renderer.instance_variable_get(:@env))
21
- ApplicationController.renderer.instance_variable_set(:@env, new_env)
22
-
23
- resources.each do |signed_params, sgid|
21
+ resources.each do |signed_params, sgid, signed_controller, url|
24
22
  selector = "[data-signed-params='#{signed_params}']"
25
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
+
26
34
  cable_ready[stream_name].outer_html(
27
35
  selector: selector,
28
- html: ApplicationController.render(resource(signed_params: signed_params, sgid: sgid))
36
+ html: html
29
37
  )
30
38
  end
31
39
 
@@ -34,12 +42,10 @@ module Futurism
34
42
 
35
43
  private
36
44
 
37
- def resource(signed_params:, sgid:)
45
+ def lookup_resource(signed_params:, sgid:)
38
46
  return GlobalID::Locator.locate_signed(sgid) if sgid.present?
39
47
 
40
- Rails
41
- .application
42
- .message_verifier("futurism")
48
+ message_verifier
43
49
  .verify(signed_params)
44
50
  .deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
45
51
  end
@@ -1,6 +1,7 @@
1
1
  module Futurism
2
2
  class Channel < ActionCable::Channel::Base
3
3
  include CableReady::Broadcaster
4
+ include Futurism::MessageVerifier
4
5
 
5
6
  def stream_name
6
7
  ids = connection.identifiers.map { |identifier| send(identifier).try(:id) || send(identifier) }
@@ -15,17 +16,24 @@ module Futurism
15
16
  end
16
17
 
17
18
  def receive(data)
18
- resources = data.fetch_values("signed_params", "sgids") { |key| [nil] }.transpose
19
+ resources = data.fetch_values("signed_params", "sgids", "signed_controllers", "urls") { |_key| Array.new(data["signed_params"].length, nil) }.transpose
19
20
 
20
- new_env = connection.env.merge(ApplicationController.renderer.instance_variable_get(:@env))
21
- ApplicationController.renderer.instance_variable_set(:@env, new_env)
22
-
23
- resources.each do |signed_params, sgid|
21
+ resources.each do |signed_params, sgid, signed_controller, url|
24
22
  selector = "[data-signed-params='#{signed_params}']"
25
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
+
26
34
  cable_ready[stream_name].outer_html(
27
35
  selector: selector,
28
- html: ApplicationController.render(resource(signed_params: signed_params, sgid: sgid))
36
+ html: html
29
37
  )
30
38
  end
31
39
 
@@ -34,12 +42,10 @@ module Futurism
34
42
 
35
43
  private
36
44
 
37
- def resource(signed_params:, sgid:)
45
+ def lookup_resource(signed_params:, sgid:)
38
46
  return GlobalID::Locator.locate_signed(sgid) if sgid.present?
39
47
 
40
- Rails
41
- .application
42
- .message_verifier("futurism")
48
+ message_verifier
43
49
  .verify(signed_params)
44
50
  .deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
45
51
  end
@@ -1,6 +1,14 @@
1
1
  module Futurism
2
2
  module Helpers
3
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
+
4
12
  placeholder = capture(&block)
5
13
 
6
14
  if records_or_string.is_a?(ActiveRecord::Base) || records_or_string.is_a?(ActiveRecord::Relation)
@@ -20,8 +28,8 @@ module Futurism
20
28
  else
21
29
  collection_class_name = collection.klass.name
22
30
  as = options.delete(:as) || collection_class_name.downcase
23
- collection.map { |record|
24
- Element.new(extends: extends, placeholder: placeholder, options: options.deep_merge(locals: {as.to_sym => record})).render
31
+ 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
25
33
  }.join.html_safe
26
34
  end
27
35
  end
@@ -35,13 +43,15 @@ module Futurism
35
43
  # wraps functionality for rendering a futurism element
36
44
  class Element
37
45
  include ActionView::Helpers
46
+ include Futurism::MessageVerifier
38
47
 
39
- attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager
48
+ attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :controller
40
49
 
41
50
  def initialize(extends:, placeholder:, options:)
42
51
  @extends = extends
43
52
  @placeholder = placeholder
44
53
  @eager = options.delete(:eager)
54
+ @controller = options.delete(:controller)
45
55
  @html_options = options.delete(:html_options) || {}
46
56
  @data_attributes = html_options.fetch(:data, {}).except(:sgid, :signed_params)
47
57
  @model = options.delete(:model)
@@ -52,7 +62,8 @@ module Futurism
52
62
  data_attributes.merge({
53
63
  signed_params: signed_params,
54
64
  sgid: model && model.to_sgid.to_s,
55
- eager: eager.presence
65
+ eager: eager.presence,
66
+ signed_controller: signed_controller
56
67
  })
57
68
  end
58
69
 
@@ -78,7 +89,13 @@ module Futurism
78
89
  private
79
90
 
80
91
  def signed_params
81
- Rails.application.message_verifier("futurism").generate(transformed_options)
92
+ message_verifier.generate(transformed_options)
93
+ end
94
+
95
+ def signed_controller
96
+ return unless controller.present?
97
+
98
+ message_verifier.generate(controller.to_s)
82
99
  end
83
100
  end
84
101
  end
@@ -1,6 +1,14 @@
1
1
  module Futurism
2
2
  module Helpers
3
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
+
4
12
  placeholder = capture(&block)
5
13
 
6
14
  if records_or_string.is_a?(ActiveRecord::Base) || records_or_string.is_a?(ActiveRecord::Relation)
@@ -35,12 +43,15 @@ module Futurism
35
43
  # wraps functionality for rendering a futurism element
36
44
  class Element
37
45
  include ActionView::Helpers
46
+ include Futurism::MessageVerifier
38
47
 
39
- attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options
48
+ attr_reader :extends, :placeholder, :html_options, :data_attributes, :model, :options, :eager, :controller
40
49
 
41
50
  def initialize(extends:, placeholder:, options:)
42
51
  @extends = extends
43
52
  @placeholder = placeholder
53
+ @eager = options.delete(:eager)
54
+ @controller = options.delete(:controller)
44
55
  @html_options = options.delete(:html_options) || {}
45
56
  @data_attributes = html_options.fetch(:data, {}).except(:sgid, :signed_params)
46
57
  @model = options.delete(:model)
@@ -50,7 +61,9 @@ module Futurism
50
61
  def dataset
51
62
  data_attributes.merge({
52
63
  signed_params: signed_params,
53
- sgid: model && model.to_sgid.to_s
64
+ sgid: model && model.to_sgid.to_s,
65
+ eager: eager.presence,
66
+ signed_controller: signed_controller
54
67
  })
55
68
  end
56
69
 
@@ -76,7 +89,13 @@ module Futurism
76
89
  private
77
90
 
78
91
  def signed_params
79
- Rails.application.message_verifier("futurism").generate(transformed_options)
92
+ message_verifier.generate(transformed_options)
93
+ end
94
+
95
+ def signed_controller
96
+ return unless controller.present?
97
+
98
+ message_verifier.generate(controller.to_s)
80
99
  end
81
100
  end
82
101
  end
@@ -0,0 +1,11 @@
1
+ module Futurism
2
+ module MessageVerifier
3
+ def self.message_verifier
4
+ @message_verifier ||= Rails.application.message_verifier("futurism")
5
+ end
6
+
7
+ def message_verifier
8
+ @message_verifier ||= Rails.application.message_verifier("futurism")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module Futurism
2
+ module Resolver
3
+ class Controller
4
+ def self.from(signed_string:)
5
+ if signed_string.present?
6
+ Futurism::MessageVerifier
7
+ .message_verifier
8
+ .verify(signed_string)
9
+ .to_s
10
+ .safe_constantize
11
+ else
12
+ Futurism.default_controller
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Futurism
2
+ module Resolver
3
+ class Controller
4
+ def self.from(signed_string:)
5
+ if signed_string.present?
6
+ Futurism::MessageVerifier
7
+ .message_verifier
8
+ .verify(signed_string)
9
+ .to_s
10
+ .safe_constantize
11
+ else
12
+ Futurism.default_controller
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ module Futurism
2
+ module Resolver
3
+ class Controller
4
+ class Renderer
5
+ def self.for(controller:, connection:, url:, params:)
6
+ new(controller: controller, connection: connection, url: url, params: params).renderer
7
+ end
8
+
9
+ def initialize(controller:, connection:, url:, params:)
10
+ @controller = controller
11
+ @connection = connection
12
+ @url = url || ""
13
+ @params = params || {}
14
+
15
+ setup_env!
16
+ end
17
+
18
+ def renderer
19
+ @renderer ||= controller.renderer
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :controller, :connection, :url, :params
25
+ attr_writer :renderer
26
+
27
+ def setup_env!
28
+ if url.present?
29
+ uri = URI.parse(url)
30
+ path = ActionDispatch::Journey::Router::Utils.normalize_path(uri.path)
31
+ query_hash = Rack::Utils.parse_nested_query(uri.query)
32
+
33
+ path_params = Rails.application.routes.recognize_path(path)
34
+
35
+ self.renderer =
36
+ renderer.new(
37
+ "rack.request.query_hash" => query_hash,
38
+ "rack.request.query_string" => uri.query,
39
+ "ORIGINAL_SCRIPT_NAME" => "",
40
+ "ORIGINAL_FULLPATH" => path,
41
+ Rack::SCRIPT_NAME => "",
42
+ Rack::PATH_INFO => path,
43
+ Rack::REQUEST_PATH => path,
44
+ Rack::QUERY_STRING => uri.query,
45
+ ActionDispatch::Http::Parameters::PARAMETERS_KEY => params.symbolize_keys.merge(path_params).merge(query_hash)
46
+ )
47
+ end
48
+
49
+ # Copy connection env to renderer to fix some RACK related issues from gems like
50
+ # Warden or Devise
51
+ new_env = connection.env.merge(renderer.instance_variable_get(:@env))
52
+ renderer.instance_variable_set(:@env, new_env)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,57 @@
1
+ module Futurism
2
+ module Resolver
3
+ class Controller
4
+ class Renderer
5
+ def self.for(controller:, connection:, url:, params:)
6
+ new(controller: controller, connection: connection, url: url, params: params).renderer
7
+ end
8
+
9
+ def initialize(controller:, connection:, url:, params:)
10
+ @controller = controller
11
+ @connection = connection
12
+ @url = url || ""
13
+ @params = params || {}
14
+
15
+ setup_env!
16
+ end
17
+
18
+ def renderer
19
+ @renderer ||= controller.renderer
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :controller, :connection, :url, :params
25
+ attr_writer :renderer
26
+
27
+ def setup_env!
28
+ if url.present?
29
+ uri = URI.parse(url)
30
+ path = ActionDispatch::Journey::Router::Utils.normalize_path(uri.path)
31
+ query_hash = Rack::Utils.parse_nested_query(uri.query)
32
+
33
+ path_params = Rails.application.routes.recognize_path(path)
34
+
35
+ self.renderer =
36
+ renderer.new(
37
+ "rack.request.query_hash" => query_hash,
38
+ "rack.request.query_string" => uri.query,
39
+ "ORIGINAL_SCRIPT_NAME" => "",
40
+ "ORIGINAL_FULLPATH" => path,
41
+ Rack::SCRIPT_NAME => "",
42
+ Rack::PATH_INFO => path,
43
+ Rack::REQUEST_PATH => path,
44
+ Rack::QUERY_STRING => uri.query,
45
+ ActionDispatch::Http::Parameters::PARAMETERS_KEY => path_params.reverse_merge(path_params)
46
+ )
47
+ end
48
+
49
+ # Copy connection env to renderer to fix some RACK related issues from gems like
50
+ # Warden or Devise
51
+ new_env = connection.env.merge(renderer.instance_variable_get(:@env))
52
+ renderer.instance_variable_set(:@env, new_env)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,3 +1,3 @@
1
1
  module Futurism
2
- VERSION = "0.5.2"
2
+ VERSION = "0.7.2"
3
3
  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.5.2
4
+ version: 0.7.2
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-10-06 00:00:00.000000000 Z
11
+ date: 2020-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -108,20 +108,6 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: spy
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: rack
127
113
  requirement: !ruby/object:Gem::Requirement
@@ -175,7 +161,6 @@ files:
175
161
  - MIT-LICENSE
176
162
  - README.md
177
163
  - Rakefile
178
- - app/assets/config/futurism_manifest.js
179
164
  - config/routes.rb
180
165
  - lib/futurism.rb
181
166
  - lib/futurism.rb~
@@ -184,6 +169,11 @@ files:
184
169
  - lib/futurism/engine.rb
185
170
  - lib/futurism/helpers.rb
186
171
  - lib/futurism/helpers.rb~
172
+ - lib/futurism/message_verifier.rb
173
+ - lib/futurism/resolver/controller.rb
174
+ - lib/futurism/resolver/controller.rb~
175
+ - lib/futurism/resolver/controller/renderer.rb
176
+ - lib/futurism/resolver/controller/renderer.rb~
187
177
  - lib/futurism/shims/deep_transform_values.rb
188
178
  - lib/futurism/version.rb
189
179
  - lib/tasks/futurism_tasks.rake
File without changes