view_component_reflex 1.7.2 → 2.1.1

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: 7e5577d7e345a14f59567739802588506a300b2caa96bba74bd4f12a93752a04
4
- data.tar.gz: be4d877d89d50555bde7f1ba929ef4fd5b1e60c72f2e7e921e33e2623574bfa7
3
+ metadata.gz: 46d3bc691137a986b26a9487624f004cf9625079040130a035129d12f3f52829
4
+ data.tar.gz: 9793628bd7618163d9c0014f887d8ef791ac25c2f168dd8c7cbb45e6c55f0839
5
5
  SHA512:
6
- metadata.gz: 60530d55c39126bd6be3d5be688f08cbe4e84e0b16e444334e438d99ec1a6b05a1f08c102407bea04b9c73ca5ace4bbb4ec013529810bc5363c6bd09e4224d70
7
- data.tar.gz: ceebff13ff2407be15ad1d3e851f78130a7bec2d00cdd260bd3ed78d32ebfce596f1763d6608b822d628f0ce91b164a53c47ee2296755733ddac01888ae1bf8c
6
+ metadata.gz: 014e4d807f719ccb361d9bd800ed599164c498c8e0a947820495982d63a1c07c7409041394d24e247701ec2510b27fe2bbfaee9829733946fe8e51ce630f8e80
7
+ data.tar.gz: 11cb4beef6ec931032d5b228fa43aee432ce8973fffa10c4391a8577d4788607937d3eb19b41650f778bbdd3cdad94f05e6eb69c0f97a7fdddc8056973f0152c
data/README.md CHANGED
@@ -108,6 +108,33 @@ def collection_key
108
108
  end
109
109
  ```
110
110
 
111
+ ### stimulate(target, data)
112
+ Stimulate another reflex from within your component. This typically requires the key of the component you're stimulating
113
+ which can be passed in via parameters.
114
+
115
+ ```ruby
116
+ def initialize(parent_key)
117
+ @parent_key = parent_key
118
+ end
119
+
120
+ def stimulate_other
121
+ stimulate("OtherComponent#method", { key: @parent_key })
122
+ end
123
+ ```
124
+
125
+ ### refresh!(selectors)
126
+ Refresh a specific element on the page. Using this will implicitly run `prevent_render!`.
127
+ If you want to render a specific element, as well as the component, a common pattern would be to pass `selector` as one of the parameters
128
+
129
+ ```
130
+ def my_method
131
+ refresh! '#my-special-element', selector
132
+ end
133
+ ```
134
+
135
+ ### selector
136
+ Returns the unique selector for this component. Useful to pass to `refresh!` when refreshing custom elements.
137
+
111
138
  ### prevent_refresh!
112
139
  By default, VCR will re-render your component after it executes your method. `revent_refresh!` prevents this from happening.
113
140
 
@@ -2,95 +2,12 @@ module ViewComponentReflex
2
2
  class Component < ViewComponent::Base
3
3
  class << self
4
4
  def init_stimulus_reflex
5
- klass = self
6
- @stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(StimulusReflex::Reflex) {
7
- def refresh!(primary_selector = "[data-controller~=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]", *selectors)
8
- save_state
9
- @channel.send :render_page_and_broadcast_morph, self, [primary_selector, *selectors], {
10
- "dataset" => element.dataset.to_h,
11
- "args" => [],
12
- "attrs" => element.attributes.to_h,
13
- "selectors" => ["body"],
14
- "target" => "#{self.class.name}##{method_name}",
15
- "url" => request.url,
16
- "permanent_attribute_name" => "data-reflex-permanent"
17
- }
18
- end
19
-
20
- def refresh_all!
21
- refresh!("body")
22
- end
23
-
24
- # SR's delegate_call_to_reflex in channel.rb
25
- # uses method to gather the method parameters, but since we're abusing
26
- # method_missing here, that'll always fail
27
- def method(name)
28
- name.to_sym.to_proc
29
- end
30
-
31
- def respond_to_missing?(name, _ = false)
32
- !!name.to_proc
33
- end
34
-
35
- before_reflex do |a|
36
- a.send a.method_name
37
- throw :abort
38
- end
39
-
40
- def method_missing(name, *args)
41
- super unless respond_to_missing?(name)
42
- state.each do |k, v|
43
- component.instance_variable_set(k, v)
44
- end
45
- name.to_proc.call(component, *args)
46
- refresh! unless @prevent_refresh
47
- end
48
-
49
- def prevent_refresh!
50
- @prevent_refresh = true
51
- end
52
-
53
- define_method :component_class do
54
- @component_class ||= klass
55
- end
56
-
57
- private :component_class
58
-
59
- private
60
-
61
- def stimulus_controller
62
- component_class.stimulus_controller
63
- end
64
-
65
- def component
66
- return @component if @component
67
- @component = component_class.allocate
68
- reflex = self
69
- exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller, :session, :prevent_refresh!]
70
- exposed_methods.each do |meth|
71
- @component.define_singleton_method(meth) do |*a|
72
- reflex.send(meth, *a)
73
- end
74
- end
75
- @component
76
- end
77
-
78
- def set_state(new_state = {})
79
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state)
80
- end
81
-
82
- def state
83
- ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
84
- end
85
-
86
- def save_state
87
- new_state = {}
88
- component.safe_instance_variables.each do |k|
89
- new_state[k] = component.instance_variable_get(k)
90
- end
91
- set_state(new_state)
92
- end
93
- })
5
+ if name.include? "::"
6
+ @stimulus_reflex ||= module_parent.const_set(name.split("::").last + "Reflex", Class.new(Reflex))
7
+ else
8
+ @stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(Reflex))
9
+ end
10
+ @stimulus_reflex.component_class = self
94
11
  end
95
12
  end
96
13
 
@@ -121,6 +38,10 @@ module ViewComponentReflex
121
38
  content_tag tag, capture(&blk), options
122
39
  end
123
40
 
41
+ def can_render_to_string?
42
+ omitted_from_state.empty?
43
+ end
44
+
124
45
  # key is required if you're using state
125
46
  # We can't initialize the session state in the initial method
126
47
  # because it doesn't have a view_context yet
@@ -174,7 +95,11 @@ module ViewComponentReflex
174
95
  else
175
96
  initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial")
176
97
  ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
177
- unless permit_parameter?(initial_state[k], instance_variable_get(k))
98
+ instance_value = instance_variable_get(k)
99
+ if permit_parameter?(initial_state[k], instance_value)
100
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, "#{@key}_initial", { k => instance_value })
101
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, @key, { k => instance_value })
102
+ else
178
103
  instance_variable_set(k, v)
179
104
  end
180
105
  end
@@ -192,7 +117,7 @@ module ViewComponentReflex
192
117
  [
193
118
  :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
194
119
  :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
195
- :@helpers, :@controller, :@request, :@content, :@tag_builder
120
+ :@helpers, :@controller, :@request, :@tag_builder
196
121
  ]
197
122
  end
198
123
 
@@ -0,0 +1,140 @@
1
+ module ViewComponentReflex
2
+ class Reflex < StimulusReflex::Reflex
3
+ include CableReady::Broadcaster
4
+
5
+ class << self
6
+ attr_accessor :component_class
7
+ end
8
+
9
+ def refresh!(primary_selector = nil, *rest)
10
+ save_state
11
+
12
+ if primary_selector.nil? && !component.can_render_to_string?
13
+ primary_selector = selector
14
+ end
15
+ if primary_selector
16
+ prevent_refresh!
17
+
18
+ controller.process(url_params[:action])
19
+ document = Nokogiri::HTML(controller.response.body)
20
+ [primary_selector, *rest].each do |s|
21
+ html = document.css(s)
22
+ if html.present?
23
+ cable_ready[channel.stream_name].morph(
24
+ selector: s,
25
+ html: html.inner_html,
26
+ children_only: true,
27
+ permanent_attribute_name: "data-reflex-permanent"
28
+ )
29
+ end
30
+ end
31
+ else
32
+ refresh_component!
33
+ end
34
+ cable_ready.broadcast
35
+ end
36
+
37
+ def refresh_component!
38
+ component.tap do |k|
39
+ k.define_singleton_method(:key) do
40
+ element.dataset[:key]
41
+ end
42
+ end
43
+ document = Nokogiri::HTML(controller.render_component_to_string(component))
44
+ cable_ready[channel.stream_name].morph(
45
+ selector: selector,
46
+ children_only: true,
47
+ html: document.css(selector).inner_html,
48
+ permanent_attribute_name: "data-reflex-permanent"
49
+ )
50
+ end
51
+
52
+ def refresh_all!
53
+ refresh!("body")
54
+ end
55
+
56
+ def selector
57
+ "[data-controller~=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]"
58
+ end
59
+
60
+ # SR's delegate_call_to_reflex in channel.rb
61
+ # uses method to gather the method parameters, but since we're abusing
62
+ # method_missing here, that'll always fail
63
+ def method(name)
64
+ name.to_sym.to_proc
65
+ end
66
+
67
+ def respond_to_missing?(name, _ = false)
68
+ !!name.to_proc
69
+ end
70
+
71
+ before_reflex do |a|
72
+ a.send a.method_name
73
+ throw :abort
74
+ end
75
+
76
+ def method_missing(name, *args)
77
+ super unless respond_to_missing?(name)
78
+ state.each do |k, v|
79
+ component.instance_variable_set(k, v)
80
+ end
81
+ name.to_proc.call(component, *args)
82
+ refresh! unless @prevent_refresh
83
+ end
84
+
85
+ def prevent_refresh!
86
+ @prevent_refresh = true
87
+ end
88
+
89
+ private
90
+
91
+ def component_class
92
+ self.class.component_class
93
+ end
94
+
95
+ def stimulus_controller
96
+ component_class.stimulus_controller
97
+ end
98
+
99
+ def stimulate(target, data)
100
+ dataset = {}
101
+ data.each do |k, v|
102
+ dataset["data-#{k}"] = v.to_s
103
+ end
104
+ channel.receive({
105
+ "target" => target,
106
+ "attrs" => element.attributes.to_h.symbolize_keys,
107
+ "dataset" => dataset
108
+ })
109
+ end
110
+
111
+ def component
112
+ return @component if @component
113
+ @component = component_class.allocate
114
+ reflex = self
115
+ exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller, :session, :prevent_refresh!, :selector, :stimulate]
116
+ exposed_methods.each do |meth|
117
+ @component.define_singleton_method(meth) do |*a|
118
+ reflex.send(meth, *a)
119
+ end
120
+ end
121
+ @component
122
+ end
123
+
124
+ def set_state(new_state = {})
125
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state)
126
+ end
127
+
128
+ def state
129
+ ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
130
+ end
131
+
132
+ def save_state
133
+ new_state = {}
134
+ component.safe_instance_variables.each do |k|
135
+ new_state[k] = component.instance_variable_get(k)
136
+ end
137
+ set_state(new_state)
138
+ end
139
+ end
140
+ end
@@ -1,7 +1,7 @@
1
1
  require "view_component_reflex/state_adapter/session"
2
2
  require "view_component_reflex/state_adapter/memory"
3
3
  require "view_component_reflex/engine"
4
- require 'stimulus_reflex'
4
+ require "stimulus_reflex"
5
5
 
6
6
  module ViewComponentReflex
7
7
  # Your code goes here...
@@ -1,3 +1,3 @@
1
1
  module ViewComponentReflex
2
- VERSION = '1.7.2'
2
+ VERSION = '2.1.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component_reflex
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.2
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua LeBlanc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-06 00:00:00.000000000 Z
11
+ date: 2020-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -69,6 +69,7 @@ files:
69
69
  - README.md
70
70
  - Rakefile
71
71
  - app/components/view_component_reflex/component.rb
72
+ - app/reflexes/view_component_reflex/reflex.rb
72
73
  - lib/view_component_reflex.rb
73
74
  - lib/view_component_reflex/engine.rb
74
75
  - lib/view_component_reflex/state_adapter/memory.rb