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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4bab60b41f78d368a46d6f5f50411b9ff3e4c1e1
4
- data.tar.gz: 162f18b085499204b190ed07b4b12e3fec03e538
3
+ metadata.gz: fe5a078a6cda01d5fde96d17f7e82f48cebb98ef
4
+ data.tar.gz: 132beffe1126ec5c30fb33153780e70c8b1505df
5
5
  SHA512:
6
- metadata.gz: 5f2abc49d9c520540967964f8c6f7badf979c80ec322dba655e69f8fcc8d9ff063d2b867cb1b47f5bdf8954c935a157ed2083f01f95b336af5452c459cb0c0f7
7
- data.tar.gz: 0e9922a471ad8e7392f48b68bec976edb67b1ea13867cf6b87436baaabee01ed3ee98727a940613d1ae47b3dbc58ddf8bdcc949dbbcfc8696bdf33597ef134ec
6
+ metadata.gz: 6c6c671b532b64fbca5e3379819c6e25c2b3d7f529c017a0d14cafb7f953743fe683a9364e8a4ea87d808808e341c32c5ee319027ea3a7930c54f4fd3ff37353
7
+ data.tar.gz: 6de0e865c112c6efac22927063cfb8d7c07eadd0b1e19c97ddcd24a4af2377ce4832593abc2bd1afd54db7e280ca26d8fb9ded2e7a3d2cf70a66c496895125f3
data/.travis.yml CHANGED
@@ -1,7 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.10
4
- - 2.2.3
5
- - 2.3.1
6
- - jruby
3
+ - 2.1
4
+ - 2.2
5
+ - 2.3
6
+ - 2.4
7
+ - jruby-9.1.14.0
7
8
  before_install: gem install bundler
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'pry'
4
+
3
5
  # Specify your gem's dependencies in grand_central.gemspec
4
6
  gemspec
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
- store.on_dispatch do |old_state, new_state|
108
- app.render unless old_state.equal?(new_state)
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 old_state.equal?(new_state)` 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.
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
@@ -10,5 +10,5 @@ require "grand_central"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
14
- IRB.start
13
+ require "pry"
14
+ Pry.start
@@ -72,7 +72,7 @@ module GrandCentral
72
72
 
73
73
  def call *args
74
74
  if store.nil?
75
- raise ArgumentError, "No store set for #{action_class}"
75
+ raise ArgumentError, "No store set for #@action_class"
76
76
  end
77
77
 
78
78
  store.dispatch @action_class.new(*handle_bowser_event(@args + args))
@@ -1,3 +1,3 @@
1
1
  module GrandCentral
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.1"
3
3
  end
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.0
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-05 00:00:00.000000000 Z
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.13
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