switcher 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - rbx-19mode
5
+ - jruby-19mode
6
+
7
+ branches:
8
+ only:
9
+ - master
data/Gemfile CHANGED
@@ -7,5 +7,6 @@ gem "guard-rspec"
7
7
 
8
8
  group "activerecord" do
9
9
  gem "activerecord", "~> 3.0.0"
10
- gem "sqlite3"
10
+ gem 'sqlite3-ruby', :platforms => :ruby
11
+ gem 'activerecord-jdbcsqlite3-adapter', :platforms => :jruby
11
12
  end
data/README.md CHANGED
@@ -1,11 +1,113 @@
1
- Not for production use
1
+ [![Build Status](https://secure.travis-ci.org/Ptico/switcher.png)](http://travis-ci.org/Ptico/switcher)
2
2
 
3
- Docs: See specs
3
+ Switcher is lightweight event-driven state machine.
4
+
5
+ ## Basic usage:
6
+
7
+ ```ruby
8
+ class Package
9
+ include Switcher::Object
10
+
11
+ def initialize(weight)
12
+ @sent_weight = weight.to_f
13
+ @tracking_number = nil
14
+ end
15
+
16
+ switcher :delivery do
17
+ state :requested do
18
+ on :send, switch_to: :sent do |ev, num|
19
+ @tracking_number = num
20
+ end
21
+ end
22
+
23
+ state :sent do
24
+ before :receive, call: :check_number
25
+
26
+ on :receive do |ev, number, weight|
27
+ if weight < (@weight - 0.3)
28
+ ev.switch_to :stolen
29
+ else
30
+ ev.switch_to :received
31
+ end
32
+ end
33
+
34
+ after :receive do |ev|
35
+ unpack if delivery_received?
36
+ end
37
+
38
+ on :miss, switch_to: :missed
39
+ end
40
+
41
+ state :received
42
+ state :stolen
43
+ state :missed
44
+ end
45
+
46
+ def check_number(ev, number, weight)
47
+ ev.stop if number.to_s != @tracking_number
48
+ end
49
+
50
+ def unpack
51
+ puts "TADA!"
52
+ end
53
+ end
54
+
55
+ package = Package.new(4.2)
56
+
57
+ package.delivery # => :requested
58
+ package.can_send? # => true
59
+ package.send! "AB123456CD"
60
+ package.delivery # => :sent
61
+ package.receive!(3.5, "CD654321AB")
62
+ package.delivery # => :sent
63
+ package.receive!(4.1, "AB123456CD")
64
+ # > TADA!
65
+
66
+ package.delivery # => :received
67
+ package.delivery_prev # => :sent
68
+ package.delivery_received? # => true
69
+ package.can_miss? # => false
70
+ ```
71
+
72
+ ## Usage with Active Record:
73
+
74
+ ```ruby
75
+ class User < ActiveRecord::Base
76
+ include Switcher::ActiveRecord
77
+
78
+ switcher :membership do
79
+ state :guest do
80
+ on :approve, switch_to: :member
81
+ end
82
+ state :member do
83
+ on :ban, switch_to: :banned do |ev, reason|
84
+ ev.stop unless reason
85
+ ban_reason = reason
86
+ end
87
+ end
88
+ state :banned do
89
+ on :unban, switch_to: :member
90
+ after :unban do
91
+ ban_reason = nil
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ user = User.find(5)
98
+ user.ban! "Stupid bastard"
99
+ user.membership # => :banned
100
+ user.save
101
+ ```
102
+
103
+ ## More examples:
104
+
105
+ * [Wiki](https://github.com/Ptico/switcher/wiki)
106
+ * [Specs](https://github.com/Ptico/switcher/tree/master/spec)
4
107
 
5
108
  ## TODO:
6
109
 
7
110
  * 1.8 compat
8
- * Write README
9
111
  * Refactoring
10
112
  * Events without state
11
113
  * Ability to define validations, associations etc. in state (ability to call static methods from object)
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ task :default => :spec
5
+
6
+ desc "Test switcher"
7
+ RSpec::Core::RakeTask.new('spec')
@@ -13,6 +13,9 @@ module Switcher
13
13
  define_method(:"#{spec_name}_prev") { self.instance_variable_get(:"@#{spec_name}_statement").state_prev }
14
14
 
15
15
  define_method(:"#{spec_name}") { self.instance_variable_get(:"@#{spec_name}_statement").state_current }
16
+ define_method(:"#{spec_name}=") { nil } # FIXME - raise exception
17
+
18
+ define_method(:"#{spec_name}_force") { |state| self.instance_variable_get(:"@#{spec_name}_statement").force_state(state.to_sym) }
16
19
 
17
20
  events = []
18
21
 
@@ -62,6 +65,8 @@ module Switcher
62
65
  end
63
66
 
64
67
  def switch_from(orig, event, *args)
68
+ return false unless (self.respond_to?(:"can_#{event}?") and self.send(:"can_#{event}?"))
69
+
65
70
  self.class.class_variable_get(:@@__specs__).each do |spc|
66
71
  spc_name = spc.name
67
72
 
@@ -72,6 +77,8 @@ module Switcher
72
77
  write_attribute("#{spc_name}_prev", self.send(:"#{spc_name}_prev"))
73
78
  end
74
79
  end
80
+
81
+ true
75
82
  end
76
83
 
77
84
  end
@@ -20,6 +20,9 @@ module Switcher
20
20
  define_method(:"#{spec_name}_prev") { self.instance_variable_get(:"@#{spec_name}_statement").state_prev }
21
21
 
22
22
  define_method(:"#{spec_name}") { self.instance_variable_get(:"@#{spec_name}_statement").state_current }
23
+ define_method(:"#{spec_name}=") { nil }
24
+
25
+ define_method(:"#{spec_name}_force") { |state| self.instance_variable_get(:"@#{spec_name}_statement").force_state(state.to_sym) }
23
26
 
24
27
  events = []
25
28
 
@@ -57,9 +60,13 @@ module Switcher
57
60
  end
58
61
 
59
62
  def switch_from(orig, event, *args)
63
+ return false unless (self.respond_to?(:"can_#{event}?") and self.send(:"can_#{event}?"))
64
+
60
65
  self.class.class_variable_get(:@@__specs__).each do |spc|
61
66
  self.instance_variable_get(:"@#{spc.name}_statement").publish(event, orig, args)
62
67
  end
68
+
69
+ true
63
70
  end
64
71
  end
65
72
  end
@@ -28,6 +28,9 @@ module Switcher
28
28
 
29
29
  def trigger(event, facade, instance, args)
30
30
  if ev = @events[event]
31
+ if ev[:options][:call]
32
+ instance.instance_exec(facade, *args) { |facad, *arguments| self.send(ev[:options][:call], facad, *arguments) }
33
+ end
31
34
  instance.instance_exec(facade, *args, &ev[:callback]) if ev[:callback].respond_to?(:call)
32
35
  if !facade.stopped && ev[:options][:allow_switch] && switch_to = ev[:options][:switch_to]
33
36
  facade.switch_to(switch_to)
@@ -51,6 +51,12 @@ module Switcher
51
51
  end
52
52
  end
53
53
 
54
+ def force_state(state)
55
+ return unless @spec.states_list.include?(state.to_sym) # FIXME - raise exception
56
+ @state_prev = state_current
57
+ @state_current = state.to_sym
58
+ end
59
+
54
60
  private
55
61
 
56
62
  def set_state(facade)
@@ -1,3 +1,3 @@
1
1
  module Switcher
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -68,9 +68,7 @@ class Human
68
68
  end
69
69
 
70
70
  state :civil do
71
- on :find_job, switch_to: :manager do |ev, cv|
72
- ev.restrict unless cv
73
- end
71
+ on :find_job, call: :employment, switch_to: :manager
74
72
  end
75
73
 
76
74
  state :manager do
@@ -89,4 +87,8 @@ class Human
89
87
  def hard_to?(learn=false)
90
88
  !!learn
91
89
  end
90
+
91
+ def employment(ev, cv)
92
+ ev.restrict unless cv
93
+ end
92
94
  end
@@ -59,6 +59,13 @@ describe Switcher do
59
59
  person.state_manager?.should be_true
60
60
  person.state_civil?.should be_false
61
61
  end
62
+
63
+ it "should force state if needed" do
64
+ crashed_car = Car.new
65
+
66
+ crashed_car.state_force(:damaged)
67
+ crashed_car.state.should eq(:damaged)
68
+ end
62
69
  end
63
70
 
64
71
  describe "Multiple states" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-04 00:00:00.000000000Z
12
+ date: 2012-03-05 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70251275731280 !ruby/object:Gem::Requirement
16
+ requirement: &70322138874120 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70251275731280
24
+ version_requirements: *70322138874120
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70251275730860 !ruby/object:Gem::Requirement
27
+ requirement: &70322138873700 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70251275730860
35
+ version_requirements: *70322138873700
36
36
  description: Switcher is simple, event-driven state machine
37
37
  email:
38
38
  - andrey@aejis.eu
@@ -41,6 +41,7 @@ extensions: []
41
41
  extra_rdoc_files: []
42
42
  files:
43
43
  - .gitignore
44
+ - .travis.yml
44
45
  - Gemfile
45
46
  - Guardfile
46
47
  - README.md