grand_central 0.5.0 → 0.5.1
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 +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
|