volute 0.1.0 → 0.1.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.
data/Rakefile CHANGED
@@ -10,11 +10,11 @@ require 'rake'
10
10
  #
11
11
  # SPEC
12
12
 
13
- task :spec do
14
- sh "spec -cfs spec/"
13
+ task :rspec do
14
+ sh 'time rspec -cfs spec/'
15
15
  end
16
16
 
17
- task :default => :spec
17
+ task :default => :rspec
18
18
 
19
19
 
20
20
  #
@@ -47,7 +47,7 @@ placing some [business] logic outside of classes
47
47
 
48
48
  #gem.add_dependency 'mime-types', '>= 1.16'
49
49
  gem.add_development_dependency 'rake'
50
- gem.add_development_dependency 'rspec'
50
+ gem.add_development_dependency 'rspec', '>= 2.0.0'
51
51
  gem.add_development_dependency 'jeweler'
52
52
 
53
53
  # gemspec spec : http://www.rubygems.org/read/chapter/20
data/TODO.txt CHANGED
@@ -1,24 +1,38 @@
1
1
 
2
2
  [o] volute 'prev' => 'new' { ... }
3
3
  [o] volute Module { ... }
4
- [o] obj.volute_do_set(:a => 0, :b => 1)
4
+ [o] obj.vset(:a => 0, :b => 1)
5
5
  [o] volute { over }
6
+ [o] volute Class { volute :attr { } } --> Class AND attr
7
+ volute Class, :attr { } --> Class OR attr
8
+ [o] volute :not, Light { ... }
9
+ [o] volute /^att/ { ... }
10
+ [o] not including Volute, but triggering Volute.xxx on some changes
11
+ [o] volute :att => :not_nil { ... } (not a nil value)
12
+ [o] volute :att => [ a, b ] { ... } (a or b)
13
+ [o] volute previous_value => [ val0, val1 ] { ... } (OR)
14
+ [o] volute [ pval0, pval1 ] => value { ... } (OR)
15
+ [o] volute :att => /regex/ { ... }
16
+ [o] volute /regex/ => /regex/ { ... }
17
+ [o] rewrite README head
18
+ [o] readme : list alternatives (aspects/hooks/callbacks)
6
19
 
7
- [ ] volute /^att/ { ... }
8
- [ ] volute /regex/ => /regex/ { ... }
9
-
10
- [ ] volute do
11
- volute Invoice do
12
- volute :paid do
13
-
14
- if is(true)
15
- object.comment = 'got paid'
16
- elsif was(nil)
17
- object.comment = 'still not paid'
18
- end
20
+ [ ] volute Invoice do
21
+ volute :paid do
22
+ if is(true)
23
+ object.comment = 'got paid'
24
+ elsif was(nil)
25
+ object.comment = 'still not paid'
19
26
  end
20
27
  end
21
28
  end
22
29
 
23
- [ ] multi-entity state machine example
30
+ [x] multi-entity state machine example
31
+ not happy with the traffic light example
32
+
33
+ [ ] volutes
34
+ `-- volute x, y
35
+ `-- volute a, b
36
+
37
+ [ ] pass the object as block binding ?
24
38
 
@@ -0,0 +1,82 @@
1
+
2
+ # license is MIT
3
+
4
+ # this example is strongly inspired by the diagnosis example
5
+ # for Ruleby ( http://github.com/codeaspects/ruleby )
6
+ # but please remember that Ruleby is a Rete implementation and is thus
7
+ # vastly superior to what is implemented here
8
+
9
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
10
+
11
+ require 'volute'
12
+
13
+
14
+ #
15
+ # some classes
16
+
17
+ class Patient
18
+
19
+ attr_reader :name
20
+
21
+ attr_accessor :fever
22
+ attr_accessor :rash
23
+ attr_accessor :spots
24
+ attr_accessor :sore_throat
25
+ attr_accessor :innoculated
26
+
27
+ attr_accessor :diagnosis
28
+
29
+ def initialize(name)
30
+
31
+ @name = name
32
+ @symptoms = {}
33
+ @innoculated = false
34
+ end
35
+
36
+ def diagnose!
37
+
38
+ Volute.apply(self)
39
+
40
+ @diagnosis
41
+ end
42
+ end
43
+
44
+ #
45
+ # the volutes
46
+
47
+ volute Patient do
48
+
49
+ # These volutes are 'state' volutes, they trigger if all the attributes
50
+ # mentioned as keys yield the given value.
51
+ # unlike classical volutes that act as OR, these states volutes have an
52
+ # AND behaviour.
53
+
54
+ volute :fever => :high, :spots => :true, :innoculated => true do
55
+ object.diagnosis = 'measles'
56
+ over # prevent further evals
57
+ end
58
+ volute :spots => true do
59
+ object.diagnosis = 'allergy (spots but no measles)'
60
+ end
61
+ volute :rash => true do
62
+ object.diagnosis = 'allergy (rash)'
63
+ end
64
+ volute :sore_throat => true, :fever => :mild do
65
+ object.diagnosis = 'flu'
66
+ end
67
+ volute :sore_throat => true, :fever => :high do
68
+ object.diagnosis = 'flu'
69
+ end
70
+ end
71
+
72
+ pat = Patient.new('alice')
73
+ pat.rash = true
74
+
75
+ puts "#{pat.name} : diagnosed with #{pat.diagnose!}"
76
+
77
+ pat = Patient.new('bob')
78
+ pat.sore_throat = true
79
+ pat.fever = :high
80
+
81
+ puts "#{pat.name} : diagnosed with #{pat.diagnose!}"
82
+
@@ -26,7 +26,7 @@ class Equation
26
26
  end
27
27
 
28
28
  #
29
- # a volute triggered for any 'set' operation on an attribute of book
29
+ # a volute per attribute
30
30
 
31
31
  volute Equation do
32
32
 
@@ -0,0 +1,27 @@
1
+
2
+ # license is MIT
3
+
4
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+
6
+ require 'volute' # gem install volute
7
+
8
+ class Light
9
+ include Volute
10
+
11
+ attr_accessor :colour
12
+ attr_accessor :changed_at
13
+ end
14
+
15
+ volute Light do
16
+ volute :colour do
17
+ object.changed_at = Time.now
18
+ end
19
+ end
20
+
21
+ l = Light.new
22
+ p l # => #<Light:0x10014c480>
23
+
24
+ l.colour = :blue
25
+ l.colour = :red
26
+ p l # => #<Light:0x10014c480 @changed_at=Fri Oct 08 20:01:52 +0900 2010, @colour=:blue>
27
+
@@ -9,50 +9,52 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
9
  require 'volute'
10
10
 
11
11
  #
12
- # our class
12
+ # our classes
13
13
 
14
- class Book
15
- include Volute
14
+ module Bookshop
16
15
 
17
- attr_accessor :stock
18
- attr_accessor :discontinued
16
+ class Book
17
+ include Volute
19
18
 
20
- attr_reader :state
19
+ attr_accessor :stock
20
+ attr_accessor :discontinued
21
+ attr_accessor :state
21
22
 
22
- def initialize (stock)
23
- @stock = stock
24
- @discontinued = false
25
- @state = :in_stock
23
+ def initialize (stock)
24
+ @stock = stock
25
+ @discontinued = false
26
+ @state = :in_stock
27
+ end
26
28
  end
27
29
  end
28
30
 
29
31
  #
30
- # a volute triggered for any 'set' operation on an attribute of book
32
+ # volutes
31
33
 
32
- volute Book do
34
+ volute Bookshop do
33
35
 
34
- # object.volute_do_set(:state, x)
35
- # is equivalent to
36
- # object.instance_variable_set(:@state, x)
36
+ volute :stock, :discontinued do
37
37
 
38
- if object.stock <= 0
39
- object.volute_do_set(
40
- :state, object.discontinued ? :discontinued : :out_of_stock)
41
- else
42
- object.volute_do_set(
43
- :state, :in_stock)
38
+ # anything in the module Bookshop that has an attribute :stock
39
+ # or :discontinued
40
+
41
+ if object.stock <= 0
42
+ object.state = object.discontinued ? :discontinued : :out_of_stock
43
+ else
44
+ object.state = :in_stock
45
+ end
44
46
  end
45
47
  end
46
48
 
47
49
  #
48
50
  # trying
49
51
 
50
- emma = Book.new(10)
52
+ emma = Bookshop::Book.new(10)
51
53
 
52
- emma.stock = 2
54
+ emma.stock -= 8
53
55
  p emma.state # => :in_stock
54
56
 
55
- emma.stock = 0
57
+ emma.stock -= 2
56
58
  p emma.state # => :out_of_stock
57
59
 
58
60
  emma.discontinued = true
@@ -0,0 +1,64 @@
1
+
2
+ # license is MIT
3
+ #
4
+ # a state-machine-ish example, inspired by the example at
5
+ # http://github.com/qoobaa/transitions
6
+
7
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+
9
+ require 'volute'
10
+
11
+ #
12
+ # our classes
13
+
14
+ module Bookshop
15
+
16
+ class Book
17
+ include Volute
18
+
19
+ attr_accessor :stock
20
+ attr_accessor :discontinued
21
+ attr_accessor :state
22
+
23
+ def initialize (stock)
24
+ @stock = stock
25
+ @discontinued = false
26
+ @state = :in_stock
27
+ end
28
+ end
29
+ end
30
+
31
+ #
32
+ # volutes
33
+
34
+ volute Bookshop do
35
+
36
+ volute :stock, :discontinued do
37
+
38
+ # anything in the module Bookshop that has an attribute :stock
39
+ # or :discontinued
40
+
41
+ volute :any => [ 0, true ] do
42
+ object.state = object.discontinued ? :discontinued : :out_of_stock
43
+ over
44
+ end
45
+ volute do
46
+ object.state = :in_stock
47
+ end
48
+ end
49
+ end
50
+
51
+ #
52
+ # trying
53
+
54
+ emma = Bookshop::Book.new(10)
55
+
56
+ emma.stock -= 8
57
+ p emma.state # => :in_stock
58
+
59
+ emma.stock -= 2
60
+ p emma.state # => :out_of_stock
61
+
62
+ emma.discontinued = true
63
+ p emma.state # => :discontinued
64
+
@@ -0,0 +1,115 @@
1
+
2
+ # license is MIT
3
+
4
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+
6
+ require 'volute'
7
+
8
+ #
9
+ # our classes
10
+
11
+ class Light
12
+ include Volute
13
+
14
+ attr_accessor :colour
15
+ attr_accessor :changed_at
16
+
17
+ def initialize(colour)
18
+ self.colour = colour
19
+ end
20
+
21
+ def green?
22
+ self.colour == :green
23
+ end
24
+
25
+ def red!
26
+ self.colour = :red
27
+ end
28
+ def green!
29
+ self.colour = :green
30
+ end
31
+
32
+ def seconds
33
+ Time.now - @changed_at
34
+ end
35
+ end
36
+
37
+ class Line
38
+ include Volute
39
+
40
+ attr_accessor :name
41
+ attr_accessor :cars
42
+ attr_accessor :light
43
+ attr_accessor :complement
44
+
45
+ def initialize(name, colour)
46
+ @name = name
47
+ @cars = 0
48
+ @light = Light.new(colour)
49
+ end
50
+
51
+ def one_car
52
+ self.cars = self.cars + 1
53
+ end
54
+
55
+ def log(message)
56
+ puts "#{@name} : #{message} (#{@cars} cars)"
57
+ end
58
+ end
59
+
60
+
61
+ #
62
+ # 'business logic'
63
+
64
+ volute Light do
65
+
66
+ volute :colour do
67
+ object.changed_at = Time.now
68
+ end
69
+ end
70
+
71
+ volute Line do
72
+
73
+ volute :cars do
74
+
75
+ delta = value - object.complement.cars
76
+
77
+ if object.light.green?
78
+ object.vset(:cars, value - 1)
79
+ object.log('cars pass')
80
+ else
81
+ if delta > 4 || object.light.seconds > 5
82
+ object.complement.light.red!
83
+ object.light.green!
84
+ object.log('now green')
85
+ object.vset(:cars, 0)
86
+ else
87
+ object.log('car stops')
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ #
94
+ # initialize system
95
+
96
+ north_south = Line.new('north-south', :green)
97
+ east_west = Line.new('east-west', :red)
98
+
99
+ north_south.complement = east_west
100
+ east_west.complement = north_south
101
+
102
+ #
103
+ # play
104
+
105
+ north_south.one_car
106
+ east_west.one_car
107
+ east_west.one_car
108
+ north_south.one_car
109
+ east_west.one_car
110
+ east_west.one_car
111
+ north_south.one_car
112
+ east_west.one_car
113
+ east_west.one_car
114
+ north_south.one_car
115
+