alter-ego-activerecord 0.1.0
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/Manifest +9 -0
- data/README.rdoc +28 -0
- data/Rakefile +19 -0
- data/alter-ego-activerecord.gemspec +37 -0
- data/lib/alter_ego/active_record_adapter.rb +30 -0
- data/test/adapter_test.rb +77 -0
- data/test/create_traffic_signals.rb +19 -0
- data/test/helper.rb +10 -0
- data/test/traffic_signal.rb +27 -0
- metadata +113 -0
data/Manifest
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
= AlterEgo-ActiveRecord
|
2
|
+
|
3
|
+
== Motivation
|
4
|
+
AlterEgo is my favorite Ruby state machine for the following reasons:
|
5
|
+
1. It's not dependent on ActiveRecord - it can be used on plain Ruby objects.
|
6
|
+
2. It most closely follows the GOF State Pattern because it allows for
|
7
|
+
polymorphic behavior based on state.
|
8
|
+
|
9
|
+
Out of the box, AlterEgo doesn't play nicely with ActiveRecord because it
|
10
|
+
stores state in <tt>@state</tt>, whereas subclasses of ActiveRecord::Base
|
11
|
+
persist their <tt>attributes</tt> hash.
|
12
|
+
|
13
|
+
This mixin overrides the AlterEgo's accessor methods for state to allow it
|
14
|
+
to be properly persisted to a database, as well as serialized/unserialized as
|
15
|
+
json, yml, and xml.
|
16
|
+
|
17
|
+
== Installation
|
18
|
+
gem install alter-ego-activerecord
|
19
|
+
|
20
|
+
== Usage
|
21
|
+
Make sure the table your class maps to has a <tt>state</tt> column.
|
22
|
+
|
23
|
+
class Example < ActiveRecord::Base
|
24
|
+
include AlterEgo # include this first
|
25
|
+
include AlterEgo::ActiveRecordAdapter
|
26
|
+
|
27
|
+
# Your code here
|
28
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('alter-ego-activerecord', '0.1.0') do |p|
|
6
|
+
p.description = "Adapter to allow ActiveRecord to persist and restore state of objects using the AlterEgo state machine"
|
7
|
+
p.author = "Paul Hieromnimon"
|
8
|
+
p.email = "paul.hieromnimon@gmail.com"
|
9
|
+
p.url = "http://github.com/pavlos/alter-ego-active-record"
|
10
|
+
p.runtime_dependencies = ["activerecord >=2.3.5", "alter-ego >=1.0.1" ]
|
11
|
+
p.ignore_pattern = ["*.sqlite"]
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake/testtask'
|
15
|
+
Rake::TestTask.new(:test) do |test|
|
16
|
+
test.libs << 'lib' << 'test'
|
17
|
+
test.pattern = 'test/**/test_*.rb'
|
18
|
+
test.verbose = true
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{alter-ego-activerecord}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Paul Hieromnimon"]
|
9
|
+
s.date = %q{2010-08-13}
|
10
|
+
s.description = %q{Adapter to allow ActiveRecord to persist and restore state of objects using the AlterEgo state machine}
|
11
|
+
s.email = %q{paul.hieromnimon@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/alter_ego/active_record_adapter.rb"]
|
13
|
+
s.files = ["Manifest", "README.rdoc", "Rakefile", "alter-ego-activerecord.gemspec", "lib/alter_ego/active_record_adapter.rb", "test/adapter_test.rb", "test/create_traffic_signals.rb", "test/helper.rb", "test/traffic_signal.rb"]
|
14
|
+
s.homepage = %q{http://github.com/pavlos/alter-ego-active-record}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Alter-ego-activerecord", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{alter-ego-activerecord}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Adapter to allow ActiveRecord to persist and restore state of objects using the AlterEgo state machine}
|
20
|
+
s.test_files = ["test/adapter_test.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 2.3.5"])
|
28
|
+
s.add_runtime_dependency(%q<alter-ego>, [">= 1.0.1"])
|
29
|
+
else
|
30
|
+
s.add_dependency(%q<activerecord>, [">= 2.3.5"])
|
31
|
+
s.add_dependency(%q<alter-ego>, [">= 1.0.1"])
|
32
|
+
end
|
33
|
+
else
|
34
|
+
s.add_dependency(%q<activerecord>, [">= 2.3.5"])
|
35
|
+
s.add_dependency(%q<alter-ego>, [">= 1.0.1"])
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AlterEgo
|
2
|
+
module ActiveRecordAdapter
|
3
|
+
|
4
|
+
def self.included klass
|
5
|
+
klass.after_initialize do |r|
|
6
|
+
r.state = r.state
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# rails needs this method to be defined for the
|
11
|
+
# class method after_find to be able to take a block
|
12
|
+
# this is because of some performance optimization
|
13
|
+
def after_initialize; end
|
14
|
+
|
15
|
+
# Override methods from AlterEgo to store state
|
16
|
+
# in @attributes["state"] instead of @state
|
17
|
+
def state=(identifier)
|
18
|
+
# state needs to always be stored as a string - symbol serialization is strange
|
19
|
+
@attributes["state"] = identifier.to_s unless identifier.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def state
|
23
|
+
result = ( @attributes["state"] || self.class.default_state)
|
24
|
+
result = result.to_sym unless result.nil?
|
25
|
+
assert(result.nil? || self.class.states.keys.include?(result))
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class AdapterTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
CreateTrafficSignals.up
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_state_stored_in_attributes
|
10
|
+
t = TrafficSignal.new
|
11
|
+
assert_not_nil t.attributes["state"]
|
12
|
+
t.state = :caution
|
13
|
+
assert_equal "caution", t.attributes["state"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_state_stored_as_string
|
17
|
+
t = TrafficSignal.new
|
18
|
+
assert_equal String, t.attributes["state"].class
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_state_returned_as_symbol
|
22
|
+
t = TrafficSignal.new
|
23
|
+
assert_equal Symbol, t.state.class
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_state_is_persisted_and_restored
|
27
|
+
t = TrafficSignal.new
|
28
|
+
old_state = t.state
|
29
|
+
t.cycle!
|
30
|
+
new_state = t.state
|
31
|
+
|
32
|
+
#sanity check
|
33
|
+
assert_not_equal old_state, new_state
|
34
|
+
|
35
|
+
t.save!
|
36
|
+
t = t.reload
|
37
|
+
|
38
|
+
assert_equal t.state, new_state
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_new_object_has_default_state
|
42
|
+
t=TrafficSignal.new
|
43
|
+
assert_equal t.state, TrafficSignal.default_state
|
44
|
+
assert_equal t.attributes["state"].to_sym, TrafficSignal.default_state
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_yaml
|
48
|
+
require 'yaml'
|
49
|
+
t= TrafficSignal.new
|
50
|
+
t.cycle!
|
51
|
+
yaml = YAML::dump t
|
52
|
+
t2 = YAML::load yaml
|
53
|
+
assert_equal t.state, t2.state
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_json
|
57
|
+
t = TrafficSignal.new
|
58
|
+
t.cycle!
|
59
|
+
json = t.to_json
|
60
|
+
t2 = TrafficSignal.new
|
61
|
+
t2.from_json json
|
62
|
+
assert_equal t.state, t2.state
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_xml
|
66
|
+
t = TrafficSignal.new
|
67
|
+
t.cycle!
|
68
|
+
xml = t.to_xml
|
69
|
+
t2 = TrafficSignal.new
|
70
|
+
t2.from_xml xml
|
71
|
+
assert_equal t.state, t2.state
|
72
|
+
end
|
73
|
+
|
74
|
+
def teardown
|
75
|
+
CreateTrafficSignals.down
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateTrafficSignals < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def self.up
|
4
|
+
create_table :traffic_signals do |t|
|
5
|
+
t.string :state
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.down
|
10
|
+
drop_table :traffic_signals
|
11
|
+
end
|
12
|
+
|
13
|
+
# redirect output so running tests doesn't display a bunch of migration info
|
14
|
+
def self.puts *args
|
15
|
+
log = File.new "test/migrations.log", 'a+'
|
16
|
+
args.each {|a| log << a.to_s; log << "\n"}
|
17
|
+
log.close
|
18
|
+
end
|
19
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'activerecord'
|
4
|
+
require 'active_record'
|
5
|
+
ActiveRecord::Base.establish_connection(:adapter=>'sqlite3', :database=>'test/testdb.sqlite')
|
6
|
+
gem 'alter-ego'
|
7
|
+
require 'alter_ego'
|
8
|
+
require 'alter_ego/active_record_adapter'
|
9
|
+
require 'create_traffic_signals'
|
10
|
+
require 'traffic_signal'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class TrafficSignal < ActiveRecord::Base
|
2
|
+
include AlterEgo
|
3
|
+
include AlterEgo::ActiveRecordAdapter
|
4
|
+
|
5
|
+
|
6
|
+
state :proceed, :default=>true do
|
7
|
+
handle :color do
|
8
|
+
"green"
|
9
|
+
end
|
10
|
+
transition :to => :caution, :on => :cycle!
|
11
|
+
end
|
12
|
+
|
13
|
+
state :caution do
|
14
|
+
handle :color do
|
15
|
+
"yellow"
|
16
|
+
end
|
17
|
+
transition :to => :stop, :on => :cycle!
|
18
|
+
end
|
19
|
+
|
20
|
+
state :stop do
|
21
|
+
handle :color do
|
22
|
+
"red"
|
23
|
+
end
|
24
|
+
transition :to => :proceed, :on => :cycle!
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alter-ego-activerecord
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Paul Hieromnimon
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-13 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activerecord
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 9
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 3
|
33
|
+
- 5
|
34
|
+
version: 2.3.5
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: alter-ego
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 21
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
- 1
|
50
|
+
version: 1.0.1
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description: Adapter to allow ActiveRecord to persist and restore state of objects using the AlterEgo state machine
|
54
|
+
email: paul.hieromnimon@gmail.com
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files:
|
60
|
+
- README.rdoc
|
61
|
+
- lib/alter_ego/active_record_adapter.rb
|
62
|
+
files:
|
63
|
+
- Manifest
|
64
|
+
- README.rdoc
|
65
|
+
- Rakefile
|
66
|
+
- alter-ego-activerecord.gemspec
|
67
|
+
- lib/alter_ego/active_record_adapter.rb
|
68
|
+
- test/adapter_test.rb
|
69
|
+
- test/create_traffic_signals.rb
|
70
|
+
- test/helper.rb
|
71
|
+
- test/traffic_signal.rb
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://github.com/pavlos/alter-ego-active-record
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options:
|
78
|
+
- --line-numbers
|
79
|
+
- --inline-source
|
80
|
+
- --title
|
81
|
+
- Alter-ego-activerecord
|
82
|
+
- --main
|
83
|
+
- README.rdoc
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 11
|
101
|
+
segments:
|
102
|
+
- 1
|
103
|
+
- 2
|
104
|
+
version: "1.2"
|
105
|
+
requirements: []
|
106
|
+
|
107
|
+
rubyforge_project: alter-ego-activerecord
|
108
|
+
rubygems_version: 1.3.7
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: Adapter to allow ActiveRecord to persist and restore state of objects using the AlterEgo state machine
|
112
|
+
test_files:
|
113
|
+
- test/adapter_test.rb
|