view_component_reflex 0.2.5 → 0.6.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: fbeda9f2f7f4f327afd6da54bcd82ac6871eab33efa07ae1901ccd8d002801d2
4
- data.tar.gz: bb5afb39f319dadfcf68272cbee6762286d475c1ce93382e611f722dd6d46baa
3
+ metadata.gz: f8bf2b7cc30e75bc01877f62705927b40e5f16fb7f763e065f33c857f8a6e9f2
4
+ data.tar.gz: 5af71af978af81acc1eef07609937e763ead6d9bbfd89dc613017342b1ffa17d
5
5
  SHA512:
6
- metadata.gz: 27d42f4f6a349223d3a0d354b31a0d37dacd51fef18450c254c8353cadb8018bc62dbccefcb99e2a5cff3bf6d61240b6836eebfb8af0974448baf57f2c4a9bc3
7
- data.tar.gz: fac906b5ad974f51b0023f04a58ba9164d38348b181a6f22e6193664d77c93ef5dd53da31abe4a94cf9f39d6ecffdd5fbf2654831f07de810c712751237c3204
6
+ metadata.gz: 7362f2ea46bf50b44f14b186ec95375a42e2a3a582e97876ab4f468eb6a13ebf755d20472a684ff8023b6cee8063ba48409d497596b95c4c435fc584c57216d9
7
+ data.tar.gz: 99be6f450fefb1bd19c5421f883b689b677d8ea6317fc140e7b01ecef9d6fcd177675509c956434506abb81c65845dc66427f1d65a577b7f0bb56a80fa67095d
data/README.md CHANGED
@@ -11,15 +11,19 @@ To add a reflex to your component, use the `reflex` method.
11
11
  ```ruby
12
12
  reflex :my_cool_reflex do
13
13
  # do stuff
14
+ refresh!
14
15
  end
15
16
  ```
16
17
 
17
18
  This will act as if you created a reflex with the method `my_cool_stuff`. To call this reflex, add `data-reflex="click->MyComponentReflex#my_cool_reflex"`, just like you're
18
19
  using stimulus reflex.
19
20
 
21
+ #####note: A reflex will not automatically re-render the component upon its completion. A component will re-render whenever the `set_state` or `refresh!` method is called.
22
+
20
23
  In addition to calling reflexes, there is a rudimentary state system. You can initialize component-local state with `initialize_state(obj)`, where `obj` is a hash.
21
24
 
22
- You can access state with the `state` helper. See the code below for an example.
25
+ You can access state with the `state` helper. See the code below for an example. Calling `set_state` will set the state,
26
+ and also re-render your component.
23
27
 
24
28
  If you're using state add `data-key="<%= key %>"` to any html element using a reflex. This
25
29
  lets ViewComponentReflex keep track of which state belongs to which component.
@@ -36,7 +40,7 @@ lets ViewComponentReflex keep track of which state belongs to which component.
36
40
  end
37
41
 
38
42
  reflex :increment do
39
- state[:count] = state[:count] + 1
43
+ set_state(count: state[:count] + 1)
40
44
  end
41
45
  end
42
46
  ```
@@ -49,6 +53,64 @@ lets ViewComponentReflex keep track of which state belongs to which component.
49
53
  </div>
50
54
  ```
51
55
 
56
+ ## Custom State Adapters
57
+
58
+ ViewComponentReflex uses session for its state by default. To change this, add
59
+ an initializer to `config/initializers/view_component_reflex.rb`.
60
+
61
+ ```ruby
62
+ ViewComponentReflex.configure do |config|
63
+ config.state_adapter = YourAdapter
64
+ end
65
+ ```
66
+
67
+ `YourAdapter` should implement
68
+
69
+ ```ruby
70
+ class YourAdapter
71
+ ##
72
+ # request - a rails request object
73
+ # key - a unique string that identifies the component instance
74
+ def self.state(request, key)
75
+ # Return state for a given key
76
+ end
77
+
78
+ ##
79
+ # reflex - The reflex instance that's trying to set the state
80
+ # key - a unique string that identifies the component
81
+ # new_state - the new state to set
82
+ def self.set_state(reflex, key, new_state)
83
+ end
84
+
85
+
86
+ ##
87
+ # request - a rails request object
88
+ # key - a unique string that identifies the component instance
89
+ # new_state - a hash containing the component state
90
+ def self.store_state(request, key, new_state = {})
91
+ # store the state
92
+ # this will be called twice, once with key, once with key_initial
93
+ # key_initial contains the initial, unmodified state.
94
+ # it should be used in reconcile_state to decide whether or not
95
+ # to re-initialize the state
96
+ end
97
+
98
+ ##
99
+ # request - a rails request object
100
+ # key - a unique string that identifies the component instance
101
+ # new_state - a hash containing the component state
102
+ def self.reconcile_state(request, key, new_state)
103
+ # The passed state should always match the initial state of the component
104
+ # if it doesn't, we need to reset the state to the passed value.
105
+ #
106
+ # This handles cases where your initialize_state param computes some value that changes
107
+ # initialize_state({ transaction: @customer.transactions.first })
108
+ # if you delete the first transaction, that ^ is no longer valid. We need to update the state.
109
+ end
110
+ end
111
+ ```
112
+
113
+
52
114
  ## Installation
53
115
  Add this line to your application's Gemfile:
54
116
 
@@ -9,7 +9,37 @@ module ViewComponentReflex
9
9
  klass = self
10
10
  @stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(StimulusReflex::Reflex) {
11
11
  def state
12
- session[element.dataset[:key]] ||= {}
12
+ ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
13
+ end
14
+
15
+ def refresh!(selectors = ["[data-controller=\"#{stimulus_controller}\"]"])
16
+ @channel.send :render_page_and_broadcast_morph, self, selectors , {
17
+ dataset: element.dataset.to_h,
18
+ args: [],
19
+ attrs: element.attributes.to_h,
20
+ selectors: ['body'],
21
+ target: "#{self.class.name}##{method_name}",
22
+ url: request.url,
23
+ permanentAttributeName: "data-reflex-permanent"
24
+ }
25
+ end
26
+
27
+ def refresh_all!
28
+ refresh!(['body'])
29
+ end
30
+
31
+ def set_state(new_state = {})
32
+ ViewComponentReflex::Engine.state_adapter.set_state(self, element.dataset[:key], new_state)
33
+ refresh!
34
+ end
35
+
36
+ before_reflex do |reflex, *args|
37
+ instance_exec(*args, &self.class.callbacks[self.method_name.to_sym]) if self.class.callbacks.include?(self.method_name.to_sym)
38
+ throw :abort
39
+ end
40
+
41
+ def self.callbacks
42
+ @callbacks ||= {}
13
43
  end
14
44
 
15
45
  define_method :stimulus_controller do
@@ -17,8 +47,8 @@ module ViewComponentReflex
17
47
  end
18
48
 
19
49
  define_singleton_method(:reflex) do |name, &blk|
50
+ callbacks[name] = blk
20
51
  define_method(name) do |*args|
21
- instance_exec(*args, &blk)
22
52
  end
23
53
  end
24
54
  })
@@ -29,6 +59,10 @@ module ViewComponentReflex
29
59
  @state = obj
30
60
  end
31
61
 
62
+ def stimulus_reflex?
63
+ helpers.controller.instance_variable_get(:@stimulus_reflex)
64
+ end
65
+
32
66
  # key is required if you're using state
33
67
  # We can't initialize the session state in the initial method
34
68
  # because it doesn't have a view_context yet
@@ -37,40 +71,17 @@ module ViewComponentReflex
37
71
  @key ||= caller.find { |p| p.include? ".html.erb" }&.hash.to_s
38
72
 
39
73
  # initialize session state
40
- if session[@key].nil?
41
- store_state(@key)
42
- store_state("#{@key}_initial")
74
+ if !stimulus_reflex? || session[@key].nil?
75
+ ViewComponentReflex::Engine.state_adapter.store_state(request, @key, @state)
76
+ ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", @state)
43
77
  else
44
- reconcile_state
78
+ # ViewComponentReflex::Engine.state_adapter.reconcile_state(request, @key, @state)
45
79
  end
46
80
  @key
47
81
  end
48
82
 
49
83
  def state
50
- session[key]
51
- end
52
-
53
- private
54
-
55
- # The passed state should always match the initial state of the component
56
- # if it doesn't, we need to reset the state to the passed value.
57
- #
58
- # This handles cases where your initialize_state param computes some value that changes
59
- # initialize_state({ transaction: @customer.transactions.first })
60
- # if you delete the first transaction, that ^ is no longer valid. We need to update the state.
61
- def reconcile_state
62
- session["#{@key}_initial"].each do |k, v|
63
- if @state[k] != v
64
- session[@key][k] = @state[k]
65
- end
66
- end
67
- end
68
-
69
- def store_state(a_key)
70
- session[a_key] = {}
71
- (@state ||= {}).each do |key, v|
72
- session[a_key][key] = v
73
- end
84
+ ViewComponentReflex::Engine.state_adapter.state(request, key)
74
85
  end
75
86
  end
76
87
  end
@@ -1,3 +1,4 @@
1
+ require "view_component_reflex/state_adapter/session"
1
2
  require "view_component_reflex/engine"
2
3
  require 'stimulus_reflex'
3
4
 
@@ -1,16 +1,13 @@
1
1
  module ViewComponentReflex
2
2
  class Engine < ::Rails::Engine
3
- config.to_prepare do
4
- class StimulusReflex::Channel < ActionCable::Channel::Base
5
- def render_page_and_broadcast_morph(reflex, selectors, data = {})
6
- html = render_page(reflex)
7
- if reflex.respond_to? :stimulus_controller
8
- selectors = ["[data-controller=\"#{reflex.stimulus_controller}\"]"]
9
- end
10
- broadcast_morphs selectors, data, html if html.present?
11
- end
12
- end
3
+ class << self
4
+ mattr_accessor :state_adapter
13
5
 
6
+ self.state_adapter = StateAdapter::Session
7
+ end
8
+
9
+ def self.configure
10
+ yield self if block_given?
14
11
  end
15
12
  end
16
13
  end
@@ -0,0 +1,38 @@
1
+ module ViewComponentReflex
2
+ module StateAdapter
3
+ class Session
4
+ def self.state(request, key)
5
+ request.session[key] ||= {}
6
+ end
7
+
8
+ def self.set_state(reflex, key, new_state)
9
+ new_state.each do |k, v|
10
+ state(reflex.request, key)[k] = v
11
+ end
12
+ store = reflex.request.session.instance_variable_get("@by")
13
+ store.commit_session reflex.request, reflex.controller.response
14
+ end
15
+
16
+ def self.store_state(request, key, new_state = {})
17
+ request.session[key] = {}
18
+ new_state.each do |k, v|
19
+ request.session[key][k] = v
20
+ end
21
+ end
22
+
23
+ # The passed state should always match the initial state of the component
24
+ # if it doesn't, we need to reset the state to the passed value.
25
+ #
26
+ # This handles cases where your initialize_state param computes some value that changes
27
+ # initialize_state({ transaction: @customer.transactions.first })
28
+ # if you delete the first transaction, that ^ is no longer valid. We need to update the state.
29
+ def self.reconcile_state(request, key, new_state)
30
+ request.session["#{key}_initial"].each do |k, v|
31
+ if new_state[k] != v
32
+ request.session[key][k] = new_state[k]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module ViewComponentReflex
2
- VERSION = '0.2.5'
2
+ VERSION = '0.6.0'
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: 0.2.5
4
+ version: 0.6.0
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-06-05 00:00:00.000000000 Z
11
+ date: 2020-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -71,6 +71,7 @@ files:
71
71
  - app/components/view_component_reflex/component.rb
72
72
  - lib/view_component_reflex.rb
73
73
  - lib/view_component_reflex/engine.rb
74
+ - lib/view_component_reflex/state_adapter/session.rb
74
75
  - lib/view_component_reflex/version.rb
75
76
  homepage: https://github.com/joshleblanc/view_component_reflex
76
77
  licenses: