view_component_reflex 0.2.3 → 0.5.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: 7f33f828c6eb7e41485294b552248fc561925c2390144fa990b4c63cf9016633
4
- data.tar.gz: 64b879ffa7759e40b2d60c805cda0f578a4210a061bdc3d9ca6ad17e0bf92629
3
+ metadata.gz: af9ea0fbad8a65f59a333d8367315adb6a13bb52f7d6dcf35fccd8ad71b8de9c
4
+ data.tar.gz: 29846c963ffb334ac158b2658566cbb3a2cb1248e8e67febd0d0a034af134b54
5
5
  SHA512:
6
- metadata.gz: d90f9f7e38d3b767b5b0370fd5cea40c3295ee01e8261e6669cfa691d003ec15066da6186ccce2e794ea0d97d8eeec1c6f0405cf5a891d5636edb7ab50ef2732
7
- data.tar.gz: c60512a9eaaf8dbb4912f35c38d4d2ca46cf2dbe3a4597be04c5200bb7a22179802d5313e366a37a6b1c56c636310f0f8c890d00f321f453f3bcdd581571c97e
6
+ metadata.gz: 99478df0b1a5ffa4784aea880b7f757299a706e0ff35a2b6a692bfecbfe64b0cd483b730c115f0574f75f5566fb50770ce2e0d079e646e5ce01113b57adfe780
7
+ data.tar.gz: d6802a9810a10dbdef2e4f82ddfeb63e766973f1151b6f87799ebfac3513e446aaaf56649c6f31f2bf24a617ece938b539e3b317354189cee1ff99488db70650
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,33 @@ 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!
16
+ @channel.render_page_and_broadcast_morph(self, nil, {
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 set_state(new_state = {})
28
+ ViewComponentReflex::Engine.state_adapter.set_state(self, element.dataset[:key], new_state)
29
+ refresh!
30
+ end
31
+
32
+ before_reflex do |reflex, *args|
33
+ instance_exec(*args, &self.class.callbacks[self.method_name.to_sym]) if self.class.callbacks.include?(self.method_name.to_sym)
34
+ throw :abort
35
+ end
36
+
37
+ def self.callbacks
38
+ @callbacks ||= {}
13
39
  end
14
40
 
15
41
  define_method :stimulus_controller do
@@ -17,8 +43,8 @@ module ViewComponentReflex
17
43
  end
18
44
 
19
45
  define_singleton_method(:reflex) do |name, &blk|
46
+ callbacks[name] = blk
20
47
  define_method(name) do |*args|
21
- instance_exec(*args, &blk)
22
48
  end
23
49
  end
24
50
  })
@@ -30,7 +56,7 @@ module ViewComponentReflex
30
56
  end
31
57
 
32
58
  # key is required if you're using state
33
- # We can't initialize the session state in the initiale method
59
+ # We can't initialize the session state in the initial method
34
60
  # because it doesn't have a view_context yet
35
61
  # This is the next best place to do it
36
62
  def key
@@ -38,16 +64,16 @@ module ViewComponentReflex
38
64
 
39
65
  # initialize session state
40
66
  if session[@key].nil?
41
- session[@key] = {}
42
- (@state ||= {}).each do |key, v|
43
- session[@key][key] = v
44
- end
67
+ ViewComponentReflex::Engine.state_adapter.store_state(request, @key, @state)
68
+ ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", @state)
69
+ else
70
+ ViewComponentReflex::Engine.state_adapter.reconcile_state(request, @key, @state)
45
71
  end
46
72
  @key
47
73
  end
48
74
 
49
75
  def state
50
- session[key]
76
+ ViewComponentReflex::Engine.state_adapter.state(request, key)
51
77
  end
52
78
  end
53
79
  end
@@ -1,18 +1,7 @@
1
+ require "view_component_reflex/state_adapter/session"
1
2
  require "view_component_reflex/engine"
2
3
  require 'stimulus_reflex'
3
4
 
4
5
  module ViewComponentReflex
5
6
  # Your code goes here...
6
7
  end
7
-
8
- module StimulusReflexChannelExtension
9
- def render_page_and_broadcast_morph(reflex, selectors, data = {})
10
- html = render_page(reflex)
11
- if reflex.respond_to? :stimulus_controller
12
- selectors = ["[data-controller=\"#{reflex.stimulus_controller}\"]"]
13
- end
14
- broadcast_morphs selectors, data, html if html.present?
15
- end
16
- end
17
-
18
- StimulusReflex::Channel.include StimulusReflexChannelExtension
@@ -1,4 +1,26 @@
1
1
  module ViewComponentReflex
2
2
  class Engine < ::Rails::Engine
3
+ class << self
4
+ mattr_accessor :state_adapter
5
+
6
+ self.state_adapter = StateAdapter::Session
7
+ end
8
+
9
+ def self.configure
10
+ yield self if block_given?
11
+ end
12
+
13
+ config.to_prepare do
14
+ class StimulusReflex::Channel < ActionCable::Channel::Base
15
+ def render_page_and_broadcast_morph(reflex, selectors, data = {})
16
+ html = render_page(reflex)
17
+ if reflex.respond_to? :stimulus_controller
18
+ selectors = ["[data-controller=\"#{reflex.stimulus_controller}\"]"]
19
+ end
20
+ broadcast_morphs selectors, data, html if html.present?
21
+ end
22
+ end
23
+
24
+ end
3
25
  end
4
26
  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.3'
2
+ VERSION = '0.5.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.3
4
+ version: 0.5.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-04 00:00:00.000000000 Z
11
+ date: 2020-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -34,16 +34,16 @@ dependencies:
34
34
  name: stimulus_reflex
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - '='
37
+ - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 3.2.2.pre1
39
+ version: '0'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - '='
44
+ - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 3.2.2.pre1
46
+ version: '0'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: view_component
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -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: