view_component_reflex 0.6.3 → 1.0.0

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: 25f3eb9ce0b0a130b3448c9aa350636e86f4497c9274a1a4ba7edfb564d11a16
4
- data.tar.gz: 809b06a816a92e8ba1e3ce0198c55c6f3d812a0ad3c4b447300c56e4733102b4
3
+ metadata.gz: f7e9a4ab35ecb85fef158f336121ffd080f26d344bbf804abfc1fd85849b6734
4
+ data.tar.gz: 202ab52b2ad27cca0b823cb13d71b0389d6dba68e35e1f92da2ef601a2690125
5
5
  SHA512:
6
- metadata.gz: e3c0cf57dda891149ccd671d02fbc967aaa83277f789a1cd0c7c75192fa4a83e3e593177ef49f904cbc9cc8bb241b9506527bfafee0b90b049252b1822a4cb68
7
- data.tar.gz: 94c5b424ae03b1f35f797ddebf0d6f24bf78b05960140c415cce86d4c2bf645ce4eef7445d7049ef57ab0d2f26925bac251b0a202199840f23f4f0adee22fc17
6
+ metadata.gz: aeccacfe56978cc599fabdba202e645950cbf09a52c1a42bcdd12620018a2299a6858c0cda5c13ad644d0e78c766cffef55d46227874ed54d4944c0cef70f042
7
+ data.tar.gz: e5e7798417649a6233513a741f7c891e05abe15c941e2ade30bb7a9200b8c9e93039ba54a656cff05bf5a1dab886dd1dfc7bfcc4b42539853ce2cac70857c606
data/README.md CHANGED
@@ -6,51 +6,32 @@ ViewComponentReflex allows you to write reflexes right in your view component co
6
6
 
7
7
  You can add reflexes to your component by adding inheriting from `ViewComponentReflex::Component`.
8
8
 
9
- To add a reflex to your component, use the `reflex` method.
10
-
11
- ```ruby
12
- reflex :my_cool_reflex do
13
- # do stuff
14
- refresh!
15
- end
16
- ```
17
-
18
9
  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
19
10
  using stimulus reflex.
20
11
 
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
-
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.
24
-
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.
27
-
28
- If you're using state add `data-key="<%= key %>"` to any html element using a reflex. This
29
- lets ViewComponentReflex keep track of which state belongs to which component.
30
-
12
+ ViewComponentReflex will maintain your component's instance variables between renders. You need to include `data-key=<%= key %>` on your root element, as well
13
+ as any element that stimulates a reflex. ViewComponent is inherently state-less, so the key is used to reconcile state to its respective component.
31
14
 
15
+ ### Example
32
16
  ```ruby
33
17
  # counter_component.rb
34
18
  class CounterComponent < ViewComponentReflex::Component
35
-
36
19
  def initialize
37
- initialize_state({
38
- count: 0
39
- })
20
+ @count = 0
40
21
  end
41
22
 
42
- reflex :increment do
43
- set_state(count: state[:count] + 1)
23
+ def increment
24
+ @count += 1
44
25
  end
45
26
  end
46
27
  ```
47
28
 
48
29
  ```erb
49
30
  # counter_component.html.erb
50
- <div data-controller="counter">
51
- <p><%= state[:count] %></p>
31
+ <%= component_controller do %>
32
+ <p><%= @count %></p>
52
33
  <button type="button" data-reflex="click->CounterComponentReflex#increment" data-key="<%= key %>">Click</button>
53
- </div>
34
+ <% end %>
54
35
  ```
55
36
 
56
37
  ## Custom State Adapters
@@ -76,19 +57,27 @@ class YourAdapter
76
57
  end
77
58
 
78
59
  ##
60
+ # set_state is used to modify the state. It accepts a reflex, which gives you
61
+ # access to the request, as well as the controller and other useful objects.
62
+ #
79
63
  # reflex - The reflex instance that's trying to set the state
80
64
  # key - a unique string that identifies the component
81
65
  # new_state - the new state to set
82
66
  def self.set_state(reflex, key, new_state)
67
+ # update the state
83
68
  end
84
69
 
85
70
 
86
71
  ##
72
+ # store_state is used to replace the state entirely. It only accepts
73
+ # a request object, rather than a reflex because it's called from the component's
74
+ # side with the component's instance variables.
75
+ #
87
76
  # request - a rails request object
88
77
  # key - a unique string that identifies the component instance
89
78
  # new_state - a hash containing the component state
90
79
  def self.store_state(request, key, new_state = {})
91
- # store the state
80
+ # replace the state
92
81
  end
93
82
  end
94
83
  ```
@@ -1,23 +1,16 @@
1
1
  module ViewComponentReflex
2
2
  class Component < ViewComponent::Base
3
3
  class << self
4
- def reflex(name, &blk)
5
- stimulus_reflex.reflex(name, &blk)
6
- end
7
-
8
- def stimulus_reflex
4
+ def init_stimulus_reflex
9
5
  klass = self
10
6
  @stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(StimulusReflex::Reflex) {
11
- def state
12
- ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
13
- end
14
-
15
- def refresh!(primary_selector = "[data-controller=\"#{stimulus_controller}\"]", *selectors)
7
+ def refresh!(primary_selector = "[data-controller=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]", *selectors)
8
+ save_state
16
9
  @channel.send :render_page_and_broadcast_morph, self, [primary_selector, *selectors], {
17
10
  dataset: element.dataset.to_h,
18
11
  args: [],
19
12
  attrs: element.attributes.to_h,
20
- selectors: ['body'],
13
+ selectors: ["body"],
21
14
  target: "#{self.class.name}##{method_name}",
22
15
  url: request.url,
23
16
  permanentAttributeName: "data-reflex-permanent"
@@ -25,60 +18,124 @@ module ViewComponentReflex
25
18
  end
26
19
 
27
20
  def refresh_all!
28
- refresh!('body')
21
+ refresh!("body")
29
22
  end
30
23
 
31
- def set_state(new_state = {}, primary_selector = nil, *selectors)
32
- ViewComponentReflex::Engine.state_adapter.set_state(self, element.dataset[:key], new_state)
33
- refresh!(primary_selector, *selectors)
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
34
29
  end
35
30
 
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)
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
38
37
  throw :abort
39
38
  end
40
39
 
41
- def self.callbacks
42
- @callbacks ||= {}
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!
47
+ end
48
+
49
+ define_method :component_class do
50
+ @component_class ||= klass
51
+ end
52
+
53
+ private :component_class
54
+
55
+ private
56
+
57
+ def stimulus_controller
58
+ component_class.stimulus_controller
59
+ end
60
+
61
+ def component
62
+ return @component if @component
63
+ @component = component_class.allocate
64
+ reflex = self
65
+ exposed_methods = [:element, :refresh!, :refresh_all!, :stimulus_controller]
66
+ exposed_methods.each do |meth|
67
+ @component.define_singleton_method(meth) do |*a|
68
+ reflex.send(meth, *a)
69
+ end
70
+ end
71
+ @component
43
72
  end
44
73
 
45
- define_method :stimulus_controller do
46
- klass.name.chomp("Component").underscore.dasherize
74
+ def set_state(new_state = {})
75
+ ViewComponentReflex::Engine.state_adapter.set_state(self, element.dataset[:key], new_state)
47
76
  end
48
77
 
49
- define_singleton_method(:reflex) do |name, &blk|
50
- callbacks[name] = blk
51
- define_method(name) do |*args|
78
+ def state
79
+ ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
80
+ end
81
+
82
+ def save_state
83
+ new_state = {}
84
+ component.instance_variables.each do |k|
85
+ new_state[k] = component.instance_variable_get(k)
52
86
  end
87
+ set_state(new_state)
53
88
  end
54
89
  })
55
90
  end
56
91
  end
57
92
 
58
- def initialize_state(obj)
59
- @state = obj
93
+ def self.stimulus_controller
94
+ name.chomp("Component").underscore.dasherize
60
95
  end
61
96
 
62
97
  def stimulus_reflex?
63
98
  helpers.controller.instance_variable_get(:@stimulus_reflex)
64
99
  end
65
100
 
101
+ def component_controller(&blk)
102
+ opts = {data: {controller: self.class.stimulus_controller, key: key}}
103
+ view_context.content_tag :div, capture(&blk), opts
104
+ end
105
+
66
106
  # key is required if you're using state
67
107
  # We can't initialize the session state in the initial method
68
108
  # because it doesn't have a view_context yet
69
109
  # This is the next best place to do it
70
110
  def key
71
- @key ||= caller.find { |p| p.include? ".html.erb" }&.hash.to_s
111
+ self.class.init_stimulus_reflex
112
+ # we want the erb file that renders the component. `caller` gives the file name,
113
+ # and line number, which should be unique. We hash it to make it a nice number
114
+ key = caller.select { |p| p.include? ".html.erb" }[1]&.hash.to_s
115
+ if @key.nil? || @key.empty?
116
+ @key = key
117
+ end
72
118
 
73
119
  # initialize session state
74
120
  if !stimulus_reflex? || session[@key].nil?
75
- ViewComponentReflex::Engine.state_adapter.store_state(request, @key, @state)
121
+ new_state = {}
122
+
123
+ # this will almost certainly break
124
+ blacklist = [
125
+ :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
126
+ :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
127
+ :@helpers, :@controller, :@request
128
+ ]
129
+ instance_variables.reject { |k| blacklist.include?(k) }.each do |k|
130
+ new_state[k] = instance_variable_get(k)
131
+ end
132
+ ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state)
133
+ else
134
+ ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
135
+ instance_variable_set(k, v)
136
+ end
76
137
  end
77
138
  @key
78
139
  end
79
-
80
- def state
81
- ViewComponentReflex::Engine.state_adapter.state(request, key)
82
- end
83
140
  end
84
141
  end
@@ -1,3 +1,3 @@
1
1
  module ViewComponentReflex
2
- VERSION = '0.6.3'
2
+ VERSION = '1.0.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.6.3
4
+ version: 1.0.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-15 00:00:00.000000000 Z
11
+ date: 2020-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails