rails_redhot 0.0.2 → 0.1.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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +99 -31
  3. data/lib/rails_redhot/version.rb +1 -1
  4. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '059048dabb167e99b923d1021bf334dc4ed59f4fb0d2306aaa199ade9e1a1b67'
4
- data.tar.gz: fcdf3b027a5d62b400ae824caeef344d1aaa91593d197d4e48a7b27c963cd4e3
3
+ metadata.gz: 5be8aff1e9314532378f476e76fe310b84bfe386abe0ba18c33ec60820696d24
4
+ data.tar.gz: f77470f8cec6b501c56fb0102e1b63242b0a11a4c453d4bbe3b3068b148b88fe
5
5
  SHA512:
6
- metadata.gz: 91d1d21a37b5b6f2b7c9e96a5757e848b53ba92430ffbc794a0ed8313281b474dbfa42369492615822753a6b0667ae235e622a8e896000ab2fc369bef9faaf40
7
- data.tar.gz: 17714334908de5714a7e18539ae264d4b937373f3e16888f5c07c91d0f3406485183c12d3bf328d65eb31ab086f8d50f48a6b6899b0f656c2c4da1c3ec9fcb8f
6
+ metadata.gz: adbb2a15f3bcffe7b910b05217d5e564125915bd39710347b527a863462fb7bef50951358d0b2d31829994410abca865f88f183abe09e09e0aef8be56956a5bf
7
+ data.tar.gz: 922d4eac6e294a8f25ac027eca82c457115884758fc785289c79867785618aa0f7a788975e5f66fcb4139bfd819cca39b4b263d6d7d8df989a6cdc03bec8f97d
data/README.md CHANGED
@@ -9,13 +9,14 @@ Or when building a search page for a webshop. Whenever the user selects a catego
9
9
  or price range to filter on this can be an action for the redux store. If the user
10
10
  hits the back button the last action can be deleted and the current state
11
11
  regenerated by reducing all remaining actions. The user only sees the last filter
12
- being reverted to what it was before.
12
+ being reverted to what it was before.
13
+
13
14
  Sometimes the actions of the user in the frontend should be sent to a backend
14
15
  application. For instance when actions of multiple users should be kept in-sync.
15
16
  Often command-query-responsibility-separation (CQRS) is used for this purpose.
16
17
  These solutions can become very complex
17
- ([example](https://medium.com/resolvejs/resolve-redux-backend-ebcfc79bbbea)
18
- scroll down a bit).
18
+ ([example](https://medium.com/resolvejs/resolve-redux-backend-ebcfc79bbbea),
19
+ scroll down a bit for a full example).
19
20
 
20
21
  The Hotwire (Html Over The Wire) approach does an excellent job of removing the need
21
22
  to build single page apps. Hotwire will be the
@@ -28,20 +29,22 @@ is still very useful for this purpose.
28
29
 
29
30
  This gem aims to combine html-over-the-wire approach with the redux pattern to
30
31
  radically reduce complexity of the overall application.
31
- (At least when compared to for instance react+cqrs.)
32
+ (At least when compared to for instance react+cqrs application stacks.)
32
33
  Only four components are required:
33
34
 
34
35
  1. Views, normal rails views rendering the current state and delivered as turbo frames
35
- 2. Actions, just (submit) buttons that send a request to the backend
36
+ 2. Actions, just submit buttons that send a request to the backend handled by a controller
36
37
  3. Store, keeping the list of actions and current state, managed by this gem and stored
37
38
  in an activerecord model
38
- 4. Reducers, a set of functions (provided by you) that translate actions to changes in state
39
+ 4. Reducers, a set of functions (provided by you) that translate actions to changes in
40
+ state. The state can be used again in step 1
39
41
 
40
42
  Common actions (undo, redo, flatten actions to initial state) are provided by this gem.
41
43
  Combined with turbo frames for rendering partial page updates this makes it easy to
42
44
  create a very smooth user experience.
43
45
 
44
46
  ## Usage
47
+ ### Model
45
48
  Create a migration to add a 'text' type attribute to a model that should have a redux store.
46
49
  In the model add an `acts_as_redux` line, specifying the name of the text attribute.
47
50
  Add a private method holding all your reducer functions.
@@ -49,40 +52,102 @@ See [this example](test/dummy/app/models/foobar.rb).
49
52
  Note that all reducer functions must return the state object (a Hash).
50
53
 
51
54
  ```ruby
52
- include RailsRedhot::ActsAsRedux
53
-
54
- acts_as_redux :my_redux
55
-
56
- private
57
-
58
- def my_redux_reducers
59
- ->(state, action) {
60
- case action[:type]
61
- when :add
62
- state[:total] += 1
63
- when :remove
64
- state[:total] -= 1
55
+ class Foobar < ApplicationRecord
56
+ include RailsRedhot::ActsAsRedux
57
+
58
+ acts_as_redux :my_redux
59
+
60
+ private
61
+
62
+ def my_redux_reducers
63
+ ->(state, action) {
64
+ case action[:type]
65
+ when :add
66
+ state[:total] += 1
67
+ when :remove
68
+ state[:total] -= 1
69
+ end
70
+ state
71
+ },
65
72
  end
66
- state
67
- },
68
73
  end
69
74
  ```
70
75
 
71
76
  Or specify your own reducer method:
72
77
 
73
78
  ```ruby
74
- acts_as_redux :my_redux, reducers: :list_of_reducers
79
+ acts_as_redux :my_redux, reducers: :my_list_of_reducers
75
80
 
76
- def list_of_reducers
81
+ def my_list_of_reducers
77
82
  # ...
78
83
  ```
79
84
 
80
- In your views add some submit buttons for adding and undoing actions.
81
- See [this example](test/dummy/app/views/foobars/_editor.html.erb).
82
- And let your controller ([example](test/dummy/app/controllers/foobars_controller.rb))
83
- process these actions.
84
- See section [Demo application](https://github.com/easydatawarehousing/rails_redhot#demo-application)
85
- for a working example.
85
+ ### Undo/redo
86
+ Every instance of the model now has access to several methods.
87
+ For undoing actions there are: `undo?`, `undo_action` and `undo!`,
88
+ which you might use in a view like this:
89
+
90
+ ```ruby
91
+ <%- if foobar.undo? %>
92
+ <%= form_with(model: foobar, url: update_action_foobar_path(foobar), method: :put) do |form| %>
93
+ <%= form.hidden_field :action, value: :undo %>
94
+ <%= form.submit "Undo: #{foobar.undo_action['type']}" %>
95
+ <% end %>
96
+ <% end %>
97
+ ```
98
+ In the controller action use the `undo!` method to perform the action.
99
+ For redoing actions the similar methods `redo?`, `redo_action` and `redo!` are available.
100
+
101
+ ### Flatten
102
+ You can 'save' the current state. Essentially this flattens the list of actions to the initial state.
103
+ Methods `flatten?` and `flatten!` can be used in a view and controller:
104
+
105
+ ```ruby
106
+ <%- if foobar.flatten? %>
107
+ <%= form_with(model: foobar, url: update_action_foobar_path(foobar), method: :put) do |form| %>
108
+ <%= form.hidden_field :action, value: :flatten %>
109
+ <%= form.submit "Save changes" %>
110
+ <% end %>
111
+ <% end %>
112
+ ```
113
+
114
+ ### Sequence ID
115
+ As a convenience a sequence ID id available which should always return a unique id
116
+ (within the context of the model instance). To get the next sequence id use `next_seq_id`,
117
+ to get the current sequence value use `seq_id`.
118
+ You could use a sequence in a reducer function to make sure every added item is assigned a unique id.
119
+
120
+ ### Dispatch actions and view state
121
+ To add an action to the store you can use the `dispatch!` method, passing a hash with the details of the action.
122
+ What the content of that hash should be is up to you.
123
+ As long as your reducer fuctions can handle the action anything is possible.
124
+
125
+ Finally to get the current state the `view_state` method is available.
126
+ In a view it can be used like so:
127
+
128
+ ```ruby
129
+ <p>
130
+ There are <%= foobar.view_state['total'] %> items
131
+ </p>
132
+
133
+ <%- foobar.view_state['items'].each do |item| %>
134
+ <p>
135
+ <%= CGI.unescape(item['value']) %>
136
+ </p>
137
+ <% end %>
138
+ ```
139
+
140
+ The `view_state` method returns a Hash. What the content of this hash looks like depends
141
+ on the reducer functions you have implemented.
142
+
143
+ For a full working example see the demo applications [view](test/dummy/app/views/foobars/_editor.html.erb)
144
+ and [controller](test/dummy/app/controllers/foobars_controller.rb).
145
+
146
+ ## Security
147
+ Care must be taken to not introduce any vulnerabilities.
148
+ When passing values from the request to the reducer functions treat any string or complex
149
+ values as potential candidates for SQL injection. Either sanitize the value or `CGI.escape`
150
+ a string before adding it to the redux store.
86
151
 
87
152
  ## Installation
88
153
  Add this line to your application's Gemfile:
@@ -113,14 +178,14 @@ rails db:setup
113
178
  rails server
114
179
  ```
115
180
 
116
- Open the [application](http://localhost:3000/foobars).
181
+ Then open the [application](http://localhost:3000/foobars).
117
182
  Click on 'New foobar', 'Add a new foobar' and 'Edit this foobar'.
118
183
 
119
184
  ## Test
120
185
  Run:
121
186
 
122
187
  ```bash
123
- rails test/dummy/test
188
+ rails test test/dummy/test
124
189
  ```
125
190
 
126
191
  ## License
@@ -134,6 +199,9 @@ The gem is available as open source under the terms of the [MIT License](https:/
134
199
  If the list of actions to process is large this would become slow.
135
200
  One would need add 'savepoints' that regularly save the state and rebuild
136
201
  the current state from that point forward
202
+ - Stricly speaking, hotwire is not needed for this gem to work. Just using
203
+ plain old rails views and controllers is fine. Hotwire certainly makes
204
+ an application using this gem a lot faster
137
205
  - No checking on the size of the text attribute used for the store is done
138
206
  - Currently only one redux store can be added to a model
139
207
  - Redux store code inspired by:
@@ -1,4 +1,4 @@
1
1
  module RailsRedhot
2
2
  # Gem version
3
- VERSION = "0.0.2"
3
+ VERSION = "0.1.0"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_redhot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivo Herweijer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-10 00:00:00.000000000 Z
11
+ date: 2021-12-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 7.0.0.rc1
19
+ version: 7.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 7.0.0.rc1
26
+ version: 7.0.0
27
27
  description: REDux pattern for HOTwire == Redhot
28
28
  email:
29
29
  - info@edwhs.nl