grand_central 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -4
- data/Gemfile +2 -0
- data/README.md +113 -3
- data/bin/console +2 -2
- data/lib/grand_central/action.rb +1 -1
- data/lib/grand_central/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe5a078a6cda01d5fde96d17f7e82f48cebb98ef
|
4
|
+
data.tar.gz: 132beffe1126ec5c30fb33153780e70c8b1505df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c6c671b532b64fbca5e3379819c6e25c2b3d7f529c017a0d14cafb7f953743fe683a9364e8a4ea87d808808e341c32c5ee319027ea3a7930c54f4fd3ff37353
|
7
|
+
data.tar.gz: 6de0e865c112c6efac22927063cfb8d7c07eadd0b1e19c97ddcd24a4af2377ce4832593abc2bd1afd54db7e280ca26d8fb9ded2e7a3d2cf70a66c496895125f3
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -93,6 +93,91 @@ store = GrandCentral::Store.new(todos: []) do |state, action|
|
|
93
93
|
end
|
94
94
|
```
|
95
95
|
|
96
|
+
### Dispatching actions without the store
|
97
|
+
|
98
|
+
There may be parts of your app that need to dispatch actions but you don't want them to know about the store itself. In a [Clearwater](https://github.com/clearwater-rb/clearwater) app, for example, your components may need to fire off actions, but you don't want them to know how to get things from the store.
|
99
|
+
|
100
|
+
Subclasses of `GrandCentral::Action` have a `store` attribute where you can set the default store to dispatch to:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
store = GrandCentral::Store.new(todos: []) do |state, action|
|
104
|
+
# ...
|
105
|
+
end
|
106
|
+
|
107
|
+
MyAction = GrandCentral::Action.with_attributes(:a, :b, :c)
|
108
|
+
MyAction.store = store
|
109
|
+
```
|
110
|
+
|
111
|
+
To use this default store, you can send the `call` message to the action class itself:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
MyAction.call(1, 2, 3) # these arguments correspond to the a, b, and c attributes above
|
115
|
+
```
|
116
|
+
|
117
|
+
#### Action Currying
|
118
|
+
|
119
|
+
You can [curry](https://en.wikipedia.org/wiki/Currying) an action with the `[]` operator instead of `call`. There is a minor difference between action currying and traditional function currying, though: function currying automatically invokes the function once all of the arguments are received, whereas currying a `GrandCentral::Action` will curry until you explicitly invoke it with `call`:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
SetAttribute = Action.with_attributes(:attr, :value)
|
123
|
+
|
124
|
+
# Returns an action type based on `SetAttribute` with `attr` hard-
|
125
|
+
# coded to `:name`. When you invoke this new action type, you only
|
126
|
+
# need to provide the value.
|
127
|
+
SetName = SetAttribute[:name]
|
128
|
+
|
129
|
+
# This action is a further specialization where we know both the
|
130
|
+
# attribute *and* the value. You don't need to provide any values.
|
131
|
+
ClearName = SetName[nil]
|
132
|
+
```
|
133
|
+
|
134
|
+
These are all equivalent action invocations:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
SetAttribute.call :name, nil
|
138
|
+
SetAttribute[:name].call nil
|
139
|
+
SetAttribute[:name, nil].call
|
140
|
+
SetName.call nil
|
141
|
+
ClearName.call
|
142
|
+
```
|
143
|
+
|
144
|
+
#### Using with Clearwater
|
145
|
+
|
146
|
+
Because of action currying, we can set action types as event handlers in Clearwater components. GrandCentral even knows how to handle `Bowser::Event` objects whose targets are instances of `Bowser::Element` (Clearwater uses Bowser as its DOM abstraction). So if you set an action to handle toggling a checkbox, the last argument will contain the input's `checked` property; if you use it for a text box, the last argument will be the `value` property. It'll even prevent `form` submission for you:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
AddTodo = Action.with_attributes(:description)
|
150
|
+
SetNewTodoDescription = Action.with_attributes(:description)
|
151
|
+
ToggleTodo = Action.with_attributes(:todo, :complete)
|
152
|
+
DeleteTodo = Action.with_attributes(:todo)
|
153
|
+
|
154
|
+
class TodoList
|
155
|
+
include Clearwater::Component
|
156
|
+
|
157
|
+
def initialize(todos, new_description)
|
158
|
+
@todos = todos
|
159
|
+
@new_description = new_description
|
160
|
+
end
|
161
|
+
|
162
|
+
def render
|
163
|
+
div([
|
164
|
+
# All arguments are provided to the AddTodo action, but we still delay its invocation
|
165
|
+
form({ onsubmit: AddTodo[@new_description] }, [
|
166
|
+
# We omit the description from SetNewTodoDescription - it's inferred from the input event
|
167
|
+
input(oninput: SetNewTodoDescription, value: @new_description),
|
168
|
+
button('Add'),
|
169
|
+
]),
|
170
|
+
ul(todos.map { |todo|
|
171
|
+
li([
|
172
|
+
input(type: :checkbox, onchange: ToggleTodo[todo]),
|
173
|
+
todo.description,
|
174
|
+
button({ onclick: DeleteTodo[todo] }, '✖️'),
|
175
|
+
])
|
176
|
+
}),
|
177
|
+
])
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
96
181
|
### Performing actions on dispatch
|
97
182
|
|
98
183
|
You may want your application to do something in response to a dispatch. For example, in a Clearwater app, you might want to re-render the application when the store's state has changed:
|
@@ -104,12 +189,37 @@ end
|
|
104
189
|
|
105
190
|
app = Clearwater::Application.new(component: Layout.new)
|
106
191
|
|
107
|
-
|
108
|
-
|
192
|
+
# on_dispatch yields the state before and after the dispatch as well as the action
|
193
|
+
store.on_dispatch do |before, after, action|
|
194
|
+
app.render unless before.equal?(after)
|
109
195
|
end
|
110
196
|
```
|
111
197
|
|
112
|
-
Notice the `unless
|
198
|
+
Notice the `unless before.equal?(after)` clause. This is one of the reasons we recommend you update state by returning a new value instead of mutating it in-place. It allows you to do cache invalidation in O(1) time.
|
199
|
+
|
200
|
+
Using `on_dispatch` also useful if you want to consolidate all of your side effects:
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
FetchUsers = Action.create do
|
204
|
+
def request
|
205
|
+
Bowser::HTTP.fetch('/api/users')
|
206
|
+
end
|
207
|
+
end
|
208
|
+
LoadUsers = Action.with_attributes(:json) do
|
209
|
+
def users
|
210
|
+
json[:users].map { |attrs| User.new(attrs) }
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
store.on_dispatch do |before, after, action|
|
215
|
+
case action
|
216
|
+
when FetchUsers
|
217
|
+
action.request
|
218
|
+
.then(&:json)
|
219
|
+
.then(&LoadUsers)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
```
|
113
223
|
|
114
224
|
## Models
|
115
225
|
|
data/bin/console
CHANGED
data/lib/grand_central/action.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grand_central
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Gaskins
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -95,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
95
|
version: '0'
|
96
96
|
requirements: []
|
97
97
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.6.
|
98
|
+
rubygems_version: 2.6.14
|
99
99
|
signing_key:
|
100
100
|
specification_version: 4
|
101
101
|
summary: State and action management for Opal apps
|