czar 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -14
- data/lib/czar/command.rb +1 -1
- data/lib/czar/version.rb +1 -1
- data/test/czar/command_test.rb +35 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ff3361aa69a60086e6d183164eb351347aa671b
|
4
|
+
data.tar.gz: 7e39e13e6b3d1db2961f1534dd7cf472aeacd779
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9daac98d456d7015df0b4d73976a2fdd7be67126384ea0346a724ff18de9ec55d5988aaedc6634437c14b1cb69dc6dfbf1275dcb67f0986063b07d075af0eeed
|
7
|
+
data.tar.gz: bc90d90e5daad1b22bae48c6000dbf256546d808172da009616fda721de9b3a9d6f744d02222922af357a91a3ba27061ba224c37387eee54e45961fbec4eff47
|
data/README.md
CHANGED
@@ -1,23 +1,45 @@
|
|
1
1
|
# Czar
|
2
2
|
|
3
3
|
Czar is a framework for building applications around the Command
|
4
|
-
pattern.
|
4
|
+
pattern. However, it is intended that these commands will stop at
|
5
|
+
various points during their lifetime, then resume again a bit later.
|
6
|
+
Maybe in response to incoming web-requests, because there's a
|
7
|
+
background timer in action, or simply because we are waiting on an
|
8
|
+
external resource.
|
5
9
|
|
6
10
|
Everything your application does is a Command of some kind; those
|
7
11
|
commands may be simple actions, sequences of actions, sequences with
|
8
|
-
decisions within them or series that trigger other commands.
|
9
|
-
|
10
|
-
|
11
|
-
is
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
decisions within them or series that trigger other commands. Czar is
|
13
|
+
intended to make those commands explicit - when your user reads some
|
14
|
+
data, makes a change to something or updates an item, each one of those
|
15
|
+
is a command flowing through your system. Representing them as their
|
16
|
+
own individual objects simplifies authorisation and can mean your code
|
17
|
+
is much simpler. For example, in a Rails app, you would often rely on
|
18
|
+
callbacks to trigger various actions on a given model. But if you use a
|
19
|
+
UpdatesGivenModel command, you can write some linear code to handle all
|
20
|
+
updates, logging, after_update handlers, instead of piecing together a
|
21
|
+
trail of callbacks. And as you can build complex commands by
|
22
|
+
aggregating multiple child commands, it also makes testing your app
|
23
|
+
simpler, as well as making its processes more explicit.
|
18
24
|
|
19
25
|
Czar can allow commands to be persisted (with a in-memory and a Redis adapter for now).
|
20
26
|
|
27
|
+
The original requirement for Czar was in an application that did an
|
28
|
+
import of products from a CSV. As this could be a very long-running
|
29
|
+
task, each operation was put onto a background queue and the system was
|
30
|
+
scaled by adding new worker processes. Each product, as it was being imported,
|
31
|
+
may have required several images to be imported from an FTP server; so
|
32
|
+
there were many tasks that could all happen in parallel: reading the
|
33
|
+
CSV, updating or adding individual products, searching for and importing
|
34
|
+
images from the FTP server, attaching imported images to products.
|
35
|
+
|
36
|
+
So the ImportsCsv command would spawn several hundred ImportsProduct commands,
|
37
|
+
and dependent upon the data, each of those could spawn several
|
38
|
+
ImportsImageFromFtpServer and AttachesImageToProducts tasks. But all the
|
39
|
+
user cares about is "when will the import be done?". So the parent ImportsCsv command keeps track
|
40
|
+
of its child tasks and, being persistent, can report its progress back
|
41
|
+
to the web application.
|
42
|
+
|
21
43
|
## Installation
|
22
44
|
|
23
45
|
Add this line to your application's Gemfile:
|
@@ -36,6 +58,7 @@ Or install it yourself as:
|
|
36
58
|
|
37
59
|
The Command module represents an implementation of the Command pattern
|
38
60
|
where each Command has an internal state machine and can optionally spawn child commands.
|
61
|
+
|
39
62
|
At its simplest, a Command will be executed, moving it from "start" state to "complete" state.
|
40
63
|
For example:
|
41
64
|
```
|
@@ -53,12 +76,12 @@ class SimpleCommand
|
|
53
76
|
end
|
54
77
|
```
|
55
78
|
|
56
|
-
To use this, simply call SimpleCommand.new.execute - the command will perform #some_complex_calculation and then be marked as :complete
|
79
|
+
To use this, simply call SimpleCommand.new.execute - the command will perform #some_complex_calculation and then be marked as :complete - we can then find out what happend by calling the #result method.
|
57
80
|
|
58
81
|
By itself, this is pretty boring. However, as we've got a simple state machine in there, we can do more interesting stuff; especially when a command is persistent.
|
59
82
|
|
60
83
|
For example:
|
61
|
-
|
84
|
+
```
|
62
85
|
class DrivesACar
|
63
86
|
include Czar::Command
|
64
87
|
|
@@ -71,7 +94,7 @@ class DrivesACar
|
|
71
94
|
if has_reached_destination?
|
72
95
|
mark_as :complete
|
73
96
|
elsif traffic_light.colour == :green
|
74
|
-
mark_as :
|
97
|
+
mark_as :moving
|
75
98
|
else
|
76
99
|
mark_as :stopped
|
77
100
|
end
|
@@ -85,6 +108,7 @@ class DrivesACar
|
|
85
108
|
# code goes here
|
86
109
|
end
|
87
110
|
end
|
111
|
+
```
|
88
112
|
|
89
113
|
In this case, we instantiate a DrivesACar command and store it somewhere. Every now and then (in response to a timer, a cron job or some other trigger) we call execute, which looks at the command's internal state and chooses if it is moving or stopped. Eventually, when we have reached our destination, the command is marked as complete. Also note, that as we do nothing when stopped, there's no need to define a stopped method.
|
90
114
|
|
@@ -92,6 +116,7 @@ Commands can also trigger child commands, and are notified when the child comple
|
|
92
116
|
|
93
117
|
For example:
|
94
118
|
|
119
|
+
```
|
95
120
|
class CourierDeliversAParcel < Struct.new(:pickup_location, :dropoff_location)
|
96
121
|
include Czar::Command
|
97
122
|
|
@@ -109,6 +134,7 @@ class CourierDeliversAParcel < Struct.new(:pickup_location, :dropoff_location)
|
|
109
134
|
end
|
110
135
|
end
|
111
136
|
end
|
137
|
+
```
|
112
138
|
|
113
139
|
## Contributing
|
114
140
|
|
data/lib/czar/command.rb
CHANGED
data/lib/czar/version.rb
CHANGED
data/test/czar/command_test.rb
CHANGED
@@ -101,6 +101,41 @@ describe Czar::Command do
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
+
describe "updating internal state" do
|
105
|
+
subject { internal_state_task.new }
|
106
|
+
|
107
|
+
it "ensures any internal state is maintained" do
|
108
|
+
subject.execute
|
109
|
+
subject.this.must_equal true
|
110
|
+
subject.execute
|
111
|
+
subject.that.must_equal true
|
112
|
+
subject.this.must_equal true
|
113
|
+
end
|
114
|
+
|
115
|
+
let(:internal_state_task) do
|
116
|
+
Class.new do
|
117
|
+
include Czar::Command
|
118
|
+
|
119
|
+
def start
|
120
|
+
mark_as :doing_stuff, this: true
|
121
|
+
end
|
122
|
+
|
123
|
+
def doing_stuff
|
124
|
+
mark_as :done_stuff, that: true
|
125
|
+
end
|
126
|
+
|
127
|
+
def this
|
128
|
+
internal[:this]
|
129
|
+
end
|
130
|
+
|
131
|
+
def that
|
132
|
+
internal[:that]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
104
139
|
describe "triggering child commands" do
|
105
140
|
subject { parent_task.new }
|
106
141
|
|
@@ -126,10 +161,6 @@ describe Czar::Command do
|
|
126
161
|
mark_as :waiting_for_child_to_complete
|
127
162
|
end
|
128
163
|
|
129
|
-
def waiting_for_child_to_complete
|
130
|
-
sleep 0.1
|
131
|
-
end
|
132
|
-
|
133
164
|
def child_task_completed child_task
|
134
165
|
mark_as :complete
|
135
166
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: czar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rahoul Baruah
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|