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 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