czar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 471ebb54150c95923d569f6ce7d32072a17974e8
4
+ data.tar.gz: 4cf441e9b83e54c5cd5f558fa03018481f6f67a5
5
+ SHA512:
6
+ metadata.gz: 0362a5ee711c89fa76765e2585ada348990f7d9fa8ce30c5d478e20a4693153c8867d7cf72e0ee4df807ac686bc68feb6c213fcb1b3ba2d2a9d34439829c7893
7
+ data.tar.gz: fc23abaa9a1d2af589d2ab0515b43fd8a403b3cfb510ab9c00839752385ee38fd6d7187fb34793792b41123613e77613ff55514da08a1b6408964beddeca9416
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in czar.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Rahoul Baruah
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Czar
2
+
3
+ Czar is a framework for building applications around the Command
4
+ pattern.
5
+
6
+ Everything your application does is a Command of some kind; those
7
+ commands may be simple actions, sequences of actions, sequences with
8
+ decisions within them or series that trigger other commands.
9
+
10
+ The advantage of using commands within, for example, a Rails application
11
+ is that you can then make your database model classes *passive* - that
12
+ is they become purely data - and the interesting stuff is all handled by
13
+ commands. No more fat models with callbacks all over the place.
14
+
15
+ Plus using commands makes it easy to ensure that authorisation is done
16
+ correctly (as each command is well-defined) and makes it trivial to add
17
+ in logging and so on.
18
+
19
+ Czar can allow commands to be persisted (with a in-memory and a Redis adapter for now).
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ gem 'czar'
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install czar
34
+
35
+ ## Usage
36
+
37
+ The Command module represents an implementation of the Command pattern
38
+ where each Command has an internal state machine and can optionally spawn child commands.
39
+ At its simplest, a Command will be executed, moving it from "start" state to "complete" state.
40
+ For example:
41
+ ```
42
+ class SimpleCommand
43
+ include Czar::Command
44
+
45
+ def start
46
+ result = some_complex_calculation
47
+ mark_as :complete, result: result
48
+ end
49
+
50
+ def some_complex_calculation
51
+ return :whatever
52
+ end
53
+ end
54
+ ```
55
+
56
+ To use this, simply call SimpleCommand.new.execute - the command will perform #some_complex_calculation and then be marked as :complete
57
+
58
+ 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
+
60
+ For example:
61
+
62
+ class DrivesACar
63
+ include Czar::Command
64
+
65
+ def start
66
+ mark_as :moving
67
+ end
68
+
69
+ def moving
70
+ move_forward
71
+ if has_reached_destination?
72
+ mark_as :complete
73
+ elsif traffic_light.colour == :green
74
+ mark_as :driving
75
+ else
76
+ mark_as :stopped
77
+ end
78
+ end
79
+
80
+ def move_forward
81
+ :vroom
82
+ end
83
+
84
+ def has_reached_destination?
85
+ # code goes here
86
+ end
87
+ end
88
+
89
+ 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
+
91
+ Commands can also trigger child commands, and are notified when the child completes.
92
+
93
+ For example:
94
+
95
+ class CourierDeliversAParcel < Struct.new(:pickup_location, :dropoff_location)
96
+ include Czar::Command
97
+
98
+ def start
99
+ perform CollectsParcel.new(pickup_location)
100
+ mark_as :waiting_for_pickup
101
+ end
102
+
103
+ def child_task_completed task
104
+ if self.state == :waiting_for_pickup
105
+ perform DeliversParcel.new(dropoff_location)
106
+ mark_as :waiting_for_dropoff
107
+ elsif self.state == :waiting_for_dropoff
108
+ mark_as :complete
109
+ end
110
+ end
111
+ end
112
+
113
+ ## Contributing
114
+
115
+ 1. Fork it ( http://github.com/<my-github-username>/czar/fork )
116
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
117
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
118
+ 4. Push to the branch (`git push origin my-new-feature`)
119
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+ require 'minitest/autorun'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = Dir["test/**/*_test.rb"]
8
+ end
9
+
10
+ desc "Run tests"
11
+ task :default => :test
data/czar.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'czar/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "czar"
8
+ spec.version = Czar::VERSION
9
+ spec.authors = ["Rahoul Baruah"]
10
+ spec.email = ["rahoul@3hv.co.uk"]
11
+ spec.summary = "A framework for implementing the Command pattern"
12
+ spec.description = "Persistent, hierarchical commands"
13
+ spec.homepage = "http://passiverecord.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest"
24
+ spec.add_development_dependency "mocha"
25
+ end
@@ -0,0 +1,53 @@
1
+ module Czar
2
+ module Command
3
+
4
+ def execute
5
+ self.send self.state if self.respond_to? self.state
6
+ end
7
+
8
+ def result
9
+ internal[:result]
10
+ end
11
+
12
+ def completed?
13
+ state == :complete
14
+ end
15
+
16
+ def state
17
+ @state ||= :start
18
+ end
19
+
20
+ def children
21
+ @children ||= []
22
+ end
23
+
24
+ protected
25
+
26
+ attr_accessor :parent
27
+
28
+ def perform child_task
29
+ child_task.parent = self
30
+ children << child_task
31
+ child_task.execute
32
+ end
33
+
34
+ def mark_as state, params = {}
35
+ @state = state.to_sym
36
+ @internal_state = params.dup
37
+ notify_parent if @state == :complete
38
+ end
39
+
40
+ def internal
41
+ @internal_state ||= {}
42
+ end
43
+
44
+ def notify_parent
45
+ parent.child_task_completed self unless parent.nil?
46
+ end
47
+
48
+ def child_task_completed child_task
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Czar
2
+ VERSION = "0.0.1"
3
+ end
data/lib/czar.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "czar/version"
2
+
3
+ module Czar
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,151 @@
1
+ require_relative '../../lib/czar/command'
2
+ require 'minitest/autorun'
3
+ require 'mocha/setup'
4
+
5
+ describe Czar::Command do
6
+
7
+ describe "initialisation" do
8
+ subject { initialiser.new }
9
+
10
+ it "is ready to start" do
11
+ subject.state.must_equal :start
12
+ end
13
+
14
+ let(:initialiser) do
15
+ Class.new do
16
+ include Czar::Command
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "performing a one stage task" do
22
+ subject { one_stage_task.new }
23
+
24
+ it "executes the start task" do
25
+ subject.execute
26
+
27
+ subject.completed?.must_equal true
28
+ subject.result.must_equal "DONE"
29
+ end
30
+
31
+ let(:one_stage_task) do
32
+ Class.new do
33
+ include Czar::Command
34
+
35
+ def start
36
+ mark_as :complete, result: "DONE"
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "performing a multi stage task" do
43
+ subject { multi_stage_task.new }
44
+
45
+ it "executes the tasks and moves through the states in order" do
46
+ subject.execute
47
+ subject.state.must_equal :in_progress
48
+
49
+ subject.execute
50
+ subject.state.must_equal :having_a_rest
51
+
52
+ subject.execute
53
+ subject.state.must_equal :having_a_rest
54
+
55
+ subject.execute
56
+ subject.completed?.must_equal true
57
+ subject.result.must_equal 'Goodbye'
58
+ end
59
+
60
+ let(:multi_stage_task) do
61
+ Class.new do
62
+ include Czar::Command
63
+
64
+ def start
65
+ mark_as :in_progress
66
+ end
67
+
68
+ def in_progress
69
+ mark_as :having_a_rest, counter: 1
70
+ end
71
+
72
+ def having_a_rest
73
+ if internal[:counter] == 1
74
+ mark_as :having_a_rest, counter: 2
75
+ else
76
+ mark_as :complete, result: 'Goodbye'
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "having states that do nothing" do
84
+ subject { do_nothing_task.new }
85
+
86
+ it "executes the first task and then does nothing" do
87
+ subject.execute
88
+ subject.state.must_equal :idle
89
+ subject.execute
90
+ subject.state.must_equal :idle
91
+ end
92
+
93
+ let(:do_nothing_task) do
94
+ Class.new do
95
+ include Czar::Command
96
+
97
+ def start
98
+ mark_as :idle
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "triggering child commands" do
105
+ subject { parent_task.new }
106
+
107
+ it "starts the child command and is not marked as complete until both it and the child have completed" do
108
+ subject.execute
109
+
110
+ child = subject.children.first
111
+ child.state.must_equal :in_progress
112
+ subject.state.must_equal :waiting_for_child_to_complete
113
+ subject.completed?.wont_equal true
114
+
115
+ child.execute
116
+ child.completed?.must_equal true
117
+ subject.completed?.must_equal true
118
+ end
119
+
120
+ let(:parent_task) do
121
+ Class.new do
122
+ include Czar::Command
123
+
124
+ def start
125
+ perform ChildTask.new
126
+ mark_as :waiting_for_child_to_complete
127
+ end
128
+
129
+ def waiting_for_child_to_complete
130
+ sleep 0.1
131
+ end
132
+
133
+ def child_task_completed child_task
134
+ mark_as :complete
135
+ end
136
+
137
+ class ChildTask
138
+ include Czar::Command
139
+
140
+ def start
141
+ mark_as :in_progress
142
+ end
143
+
144
+ def in_progress
145
+ mark_as :complete
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: czar
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Rahoul Baruah
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mocha
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Persistent, hierarchical commands
70
+ email:
71
+ - rahoul@3hv.co.uk
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - czar.gemspec
82
+ - lib/czar.rb
83
+ - lib/czar/command.rb
84
+ - lib/czar/version.rb
85
+ - test/czar/command_test.rb
86
+ homepage: http://passiverecord.com
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.2.1
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: A framework for implementing the Command pattern
110
+ test_files:
111
+ - test/czar/command_test.rb