RubyExt 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/README +846 -0
  2. data/lib/RubyExt/Cache.rb +101 -0
  3. data/lib/RubyExt/Cache.res/multiple_version_with_args.txt +12 -0
  4. data/lib/RubyExt/Cache.res/multiple_version_without_args.txt +11 -0
  5. data/lib/RubyExt/Cache.res/single_version_with_args.txt +11 -0
  6. data/lib/RubyExt/Cache.res/single_version_without_args.txt +10 -0
  7. data/lib/RubyExt/ClassLoader.rb +142 -0
  8. data/lib/RubyExt/Debug.rb +24 -0
  9. data/lib/RubyExt/ExtraBlankSlate.rb +14 -0
  10. data/lib/RubyExt/ImportAll.rb +9 -0
  11. data/lib/RubyExt/Localization/Module.rb +12 -0
  12. data/lib/RubyExt/Localization/Object.rb +19 -0
  13. data/lib/RubyExt/Localization/require.rb +3 -0
  14. data/lib/RubyExt/Localization.rb +28 -0
  15. data/lib/RubyExt/Log.config.sample +57 -0
  16. data/lib/RubyExt/Log.rb +59 -0
  17. data/lib/RubyExt/OpenConstructor.rb +51 -0
  18. data/lib/RubyExt/Path.rb +91 -0
  19. data/lib/RubyExt/Resource/file_system_provider.rb +244 -0
  20. data/lib/RubyExt/StateMashine.rb +176 -0
  21. data/lib/RubyExt/Synchronizer.rb +29 -0
  22. data/lib/RubyExt/array.rb +24 -0
  23. data/lib/RubyExt/assert.rb +111 -0
  24. data/lib/RubyExt/class.rb +0 -0
  25. data/lib/RubyExt/debug.rb +71 -0
  26. data/lib/RubyExt/false_class.rb +5 -0
  27. data/lib/RubyExt/file.rb +23 -0
  28. data/lib/RubyExt/ideas.txt +17 -0
  29. data/lib/RubyExt/kernel.rb +61 -0
  30. data/lib/RubyExt/module.rb +102 -0
  31. data/lib/RubyExt/nil_class.rb +5 -0
  32. data/lib/RubyExt/object.rb +7 -0
  33. data/lib/RubyExt/observable.rb +25 -0
  34. data/lib/RubyExt/require.rb +56 -0
  35. data/lib/RubyExt/require_base.rb +35 -0
  36. data/lib/RubyExt/resource.rb +198 -0
  37. data/lib/RubyExt/string.rb +21 -0
  38. data/lib/RubyExt/symbol.rb +21 -0
  39. data/lib/RubyExt/true_class +0 -0
  40. data/lib/RubyExt/true_class.rb +5 -0
  41. data/rakefile +49 -0
  42. data/spec/RubyExt/ForClassLoader/ClassA.rb +4 -0
  43. data/spec/RubyExt/ForClassLoader/LoadCount.rb +1 -0
  44. data/spec/RubyExt/ForClassLoader/ModuleA/ClassB.rb +3 -0
  45. data/spec/RubyExt/ForClassLoader/ModuleA/ClassInsideAnonymousClass.rb +3 -0
  46. data/spec/RubyExt/ForClassLoader/ModuleA/InfinityLoop.rb +3 -0
  47. data/spec/RubyExt/ForClassLoader/ModuleA/ModuleB/ClassC.rb +3 -0
  48. data/spec/RubyExt/ForClassLoader/ModuleA/SameName/SomeClass.rb +2 -0
  49. data/spec/RubyExt/ForClassLoader/ModuleA/SameName.rb +2 -0
  50. data/spec/RubyExt/ForClassLoader/Scope/ScopeA1.rb +3 -0
  51. data/spec/RubyExt/ForClassLoader/Scope/ScopeB1.rb +5 -0
  52. data/spec/RubyExt/ForKernel/Raise2.rb +5 -0
  53. data/spec/RubyExt/ForKernel/RaiseWithoutSelf.rb +9 -0
  54. data/spec/RubyExt/ForLocalization/NS/A.rb +3 -0
  55. data/spec/RubyExt/ForLocalization/NS/A.ru.localization.yaml +2 -0
  56. data/spec/RubyExt/ForLocalization/NS/B.rb +22 -0
  57. data/spec/RubyExt/ForLocalization/NS/B.ru.localization.yaml +3 -0
  58. data/spec/RubyExt/ForLocalization/NS.ru.localization.yaml +2 -0
  59. data/spec/RubyExt/ForModule/NS1/A.data +1 -0
  60. data/spec/RubyExt/ForModule/NS1/A.rb +1 -0
  61. data/spec/RubyExt/ForModule/NS1/A2.rb +1 -0
  62. data/spec/RubyExt/ForModule/NS1/B.rb +2 -0
  63. data/spec/RubyExt/ForModule/NS1/B2.rb +1 -0
  64. data/spec/RubyExt/ForModule/NS1.data +1 -0
  65. data/spec/RubyExt/ForModule/NS2/A.data +1 -0
  66. data/spec/RubyExt/ForModule/NS2/A.rb +3 -0
  67. data/spec/RubyExt/ForModule/NS2/B.rb +3 -0
  68. data/spec/RubyExt/ForModule/NS2/M.data +1 -0
  69. data/spec/RubyExt/ForModule/NS2/M.rb +3 -0
  70. data/spec/RubyExt/ForResource/ChangedClass.rb +1 -0
  71. data/spec/RubyExt/ForResource/ChangedClass.res/Text.txt +0 -0
  72. data/spec/RubyExt/ForResource/ChangedClass.txt +0 -0
  73. data/spec/RubyExt/ForResource/ProviderChaining/ProviderABaseDir/ChainTest.rb +3 -0
  74. data/spec/RubyExt/ForResource/ProviderChaining/ProviderABaseDir/ChainTest.resource +1 -0
  75. data/spec/RubyExt/ForResource/ProviderChaining/ProviderABaseDir/ClassExistOnlyInProviderA.rb +3 -0
  76. data/spec/RubyExt/ForResource/ProviderChaining/ProviderBBaseDir/ChainTest.rb +3 -0
  77. data/spec/RubyExt/ForResource/ProviderChaining/ProviderBBaseDir/ChainTest.resource +1 -0
  78. data/spec/RubyExt/ForResource/ResourceExtension.rb +1 -0
  79. data/spec/RubyExt/ForResource/ResourceExtension.res/Data.yaml +2 -0
  80. data/spec/RubyExt/ForResource/ResourceTest/Test.rb +1 -0
  81. data/spec/RubyExt/ForResource/ResourceTest/Test.res/Data.txt +1 -0
  82. data/spec/RubyExt/ForResource/ResourceTest/Test.res/txt +1 -0
  83. data/spec/array_spec.rb +27 -0
  84. data/spec/assert_spec.rb +18 -0
  85. data/spec/cache_spec.rb +95 -0
  86. data/spec/class_loader_spec.rb +94 -0
  87. data/spec/import_all_spec.rb +21 -0
  88. data/spec/kernel_spec.rb +38 -0
  89. data/spec/localization_spec.rb +49 -0
  90. data/spec/module_spec.rb +135 -0
  91. data/spec/object_spec.rb +8 -0
  92. data/spec/observable_spec.rb +44 -0
  93. data/spec/open_constructor_spec.rb +38 -0
  94. data/spec/path_spec.rb +169 -0
  95. data/spec/resource_spec.rb +135 -0
  96. data/spec/state_machine_spec.rb +201 -0
  97. data/spec/synchronizer_spec.rb +66 -0
  98. metadata +130 -10
  99. data/lib/RubyExt.rb +0 -1
  100. data/lib/hello_gem.rb +0 -5
@@ -0,0 +1,38 @@
1
+ require 'RubyExt/require_base'
2
+ require 'spec'
3
+
4
+ module RubyExt
5
+ describe 'OpenConstructor' do
6
+ class Test
7
+ include OpenConstructor
8
+ attr_accessor :name, :value
9
+ end
10
+
11
+ it 'should initialize atributes from Hash' do
12
+ t = Test.new.set(:name => 'name', :value => 'value')
13
+ [t.name, t.value].should == ['name', 'value']
14
+ end
15
+
16
+ it 'should initialize atributes from any Object' do
17
+ t = Test.new.set(:name => 'name', :value => 'value')
18
+ t2 = Test.new.set t
19
+ [t2.name, t2.value].should == ['name', 'value']
20
+ end
21
+
22
+ it 'restrict copied values' do
23
+ t = Test.new.set(:name => 'name', :value => 'value')
24
+ t2 = Test.new.set t, [:name]
25
+ [t2.name, t2.value].should == ['name', nil]
26
+
27
+ t = {:name => 'name', :value => 'value'}
28
+ t2 = Test.new.set t, [:name]
29
+ [t2.name, t2.value].should == ['name', nil]
30
+ end
31
+
32
+ it 'to_hash' do
33
+ h = {:name => 'name', :value => 'value'}
34
+ t = Test.new.set h
35
+ t.to_hash.should == h
36
+ end
37
+ end
38
+ end
data/spec/path_spec.rb ADDED
@@ -0,0 +1,169 @@
1
+ require 'RubyExt/require_base'
2
+ require 'spec'
3
+
4
+ module RubyExt
5
+ describe "Path" do
6
+ it '+' do
7
+ (Path.new('/aa')+'bb').should == Path.new('/aa/bb')
8
+ (Path.new('aa')+'bb').should == Path.new('aa/bb')
9
+ (Path.new('/aa')+Path.new('bb')).should == Path.new('/aa/bb')
10
+ end
11
+
12
+ it "Invaalid path" do
13
+ lambda{Path.new '//aa'}.should raise_error
14
+ lambda{Path.new '//'}.should raise_error
15
+ lambda{Path.new '/aa//bb'}.should raise_error
16
+ lambda{Path.new 'aa//bb'}.should raise_error
17
+ end
18
+
19
+ it "simple?" do
20
+ Path.new('aa').should be_simple
21
+ Path.new('/aa').should be_simple
22
+ Path.new('').should be_simple
23
+ Path.new('/').should be_simple
24
+
25
+ Path.new('aa/bb').should_not be_simple
26
+ Path.new('/aa/bb').should_not be_simple
27
+ end
28
+
29
+ it "should bbe cconvertaabble to String" do
30
+ Path.new('aa').to_s.class.should == String
31
+ end
32
+
33
+ it 'bbaase operaations' do
34
+ Path.new('/').empty?.should be_true
35
+ Path.new('aa').absolute?.should be_false
36
+ Path.new('/aa').absolute?.should be_true
37
+ end
38
+
39
+ it 'first && last' do
40
+ path = Path.new("/aa/bb/cc")
41
+ path.first.should == Path.new('/aa')
42
+ path.last.should == Path.new('/cc')
43
+
44
+ path = Path.new("/")
45
+ path.should == path.first
46
+ path.should == path.last
47
+
48
+ path = Path.new("aa/bb/cc")
49
+ Path.new('aa').should == path.first
50
+ Path.new('cc').should == path.last
51
+
52
+ path = Path.new("")
53
+ path.should == path.first
54
+ path.should == path.last
55
+ end
56
+
57
+ it 'first_name & last_name' do
58
+ path = Path.new("/aa/bb/cc")
59
+ path.last_name.should == 'cc'
60
+ path.first_name.should == 'aa'
61
+
62
+ path = Path.new("/")
63
+ path.last_name.should == nil
64
+ path.first_name.should == nil
65
+
66
+ path = Path.new("aa/bb/cc")
67
+ path.last_name.should == 'cc'
68
+ path.first_name.should == 'aa'
69
+
70
+ path = Path.new("")
71
+ path.last_name.should == nil
72
+ path.first_name.should == nil
73
+ end
74
+
75
+ it "next & previous" do
76
+ path = Path.new("/aa/bb/cc")
77
+
78
+ path.previous.should == Path.new('/aa/bb')
79
+ path.previous.previous.previous.should == Path.new("/")
80
+ path.previous.previous.previous.previous.should == nil
81
+
82
+ path.next.should == Path.new('/bb/cc')
83
+ path.next.next.next.should == nil
84
+
85
+ path = Path.new("aa/bb/cc")
86
+
87
+ path.previous.should == Path.new('aa/bb')
88
+ path.previous.previous.previous.should == Path.new
89
+ path.previous.previous.previous.previous.should == nil
90
+
91
+ path.next.should == Path.new('bb/cc')
92
+ path.next.next.next.should == nil
93
+ end
94
+
95
+ it 'aabbsolute path' do
96
+ path = Path.new("/aa/bb/cc")
97
+
98
+ str = ''
99
+ path.each do |p|
100
+ str << p.to_s
101
+ end
102
+ str.should == 'aabbcc'
103
+
104
+ path.size.should == 3
105
+
106
+ Path.new("/bb/cc").should == "/bb/cc"
107
+
108
+ path.after('aa').should == '/bb/cc'
109
+ path.after('bb').should == '/cc'
110
+ path.after('cc').should == '/'
111
+ path.before('aa').should == '/'
112
+ path.before('bb').should == '/aa'
113
+ path.before('cc').should == '/aa/bb'
114
+ end
115
+
116
+ it 'should cconvert to relative' do
117
+ Path.new("aa").should == Path.new("/aa").to_relative
118
+ end
119
+
120
+ it 'should cconvert to String' do
121
+ Path.new("/").to_s.should == "/"
122
+ Path.new("").to_s.should == ""
123
+ end
124
+
125
+ it 'should define ==' do
126
+ Path.new("aa/bb").should_not == Path.new("aa")
127
+ end
128
+
129
+ it "cconstrucctors should works" do
130
+ Path.new("")
131
+ Path.new("/")
132
+ end
133
+
134
+ it "should aappend to end aand staart" do
135
+ path = Path.new("")
136
+ path = path.add(Path.new("aa"))
137
+ path.should == Path.new("aa")
138
+
139
+ path = Path.new("bb")
140
+ path = path.add(Path.new("aa"))
141
+ path.should == Path.new("bb/aa")
142
+
143
+ path = Path.new("/")
144
+ path = path.add(Path.new("aa"))
145
+ path.should == Path.new("/aa")
146
+ end
147
+
148
+ it "relative path" do
149
+ path = Path.new("aa/bb/cc")
150
+
151
+ path.size.should == 3
152
+
153
+ str = ''
154
+ path.each do |p|
155
+ str << p.to_s
156
+ end
157
+ str.should == 'aabbcc'
158
+
159
+ Path.new("bb/cc").should == "bb/cc"
160
+
161
+ path.after('aa').should == 'bb/cc'
162
+ path.after('bb').should == 'cc'
163
+ path.after('cc').should == ''
164
+ path.before('aa').should == ''
165
+ path.before('bb').should == 'aa'
166
+ path.before('cc').should == 'aa/bb'
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,135 @@
1
+ require 'RubyExt/require_base'
2
+ require 'spec'
3
+ Thread.abort_on_exception=true
4
+
5
+ module RubyExt
6
+ describe "Resource" do
7
+
8
+ before :all do
9
+ dir = "#{File.dirname(__FILE__)}/RubyExt/ForResource/ProviderChaining"
10
+ Resource.add_resource_provider RubyExt::FileSystemProvider.new(lambda{["#{dir}/ProviderABaseDir"]})
11
+ Resource.add_resource_provider RubyExt::FileSystemProvider.new(lambda{["#{dir}/ProviderBBaseDir"]})
12
+ end
13
+
14
+ it "class get, set, exist, delete" do
15
+ Resource.class_set("RubyExt::ForResource::ClassTest",
16
+ "class ClassTest; end", File.dirname(__FILE__))
17
+
18
+ Resource.class_exist?("RubyExt::ForResource::ClassTest").
19
+ should be_true
20
+
21
+ Resource.class_get("RubyExt::ForResource::ClassTest").
22
+ should == "class ClassTest; end"
23
+
24
+ Resource.class_delete("RubyExt::ForResource::ClassTest")
25
+ Resource.class_exist?("RubyExt::ForResource::ClassTest").
26
+ should be_false
27
+
28
+ end
29
+
30
+ it "namespace" do
31
+ Resource.class_exist?("RubyExt::ForResource").should be_true
32
+ end
33
+
34
+ it "resource get, set, delete, exist" do
35
+ dir = "#{File.dirname(__FILE__)}/RubyExt/ForResource/ResourceTest"
36
+ FileUtils.remove_dir(dir) if File.exist? dir
37
+
38
+ FileUtils.mkdir dir
39
+ File.write("#{dir}/Test.rb", "class Test; end")
40
+ File.write("#{dir}/Test.txt", "Test.txt")
41
+ FileUtils.mkdir "#{dir}/Test.res"
42
+ File.write("#{dir}/Test.res/Data.txt", "Data.txt")
43
+
44
+ Resource.resource_get(RubyExt::ForResource::ResourceTest::Test, "txt").
45
+ should == "Test.txt"
46
+ Resource.resource_get(RubyExt::ForResource::ResourceTest::Test, "Data.txt").
47
+ should == "Data.txt"
48
+
49
+ Resource.resource_exist?(RubyExt::ForResource::ResourceTest::Test, "txt").
50
+ should be_true
51
+ Resource.resource_exist?(RubyExt::ForResource::ResourceTest::Test, "Data.txt").
52
+ should be_true
53
+
54
+ Resource.resource_delete(RubyExt::ForResource::ResourceTest::Test, "txt")
55
+ Resource.resource_delete(RubyExt::ForResource::ResourceTest::Test, "Data.txt")
56
+ Resource.resource_exist?(RubyExt::ForResource::ResourceTest::Test, "txt").
57
+ should be_false
58
+ Resource.resource_exist?(RubyExt::ForResource::ResourceTest::Test, "Data.txt").
59
+ should be_false
60
+
61
+
62
+ Resource.resource_set(RubyExt::ForResource::ResourceTest::Test, "txt",
63
+ "Test.txt")
64
+ Resource.resource_set(RubyExt::ForResource::ResourceTest::Test, "Data.txt",
65
+ "Data.txt")
66
+ Resource.resource_get(RubyExt::ForResource::ResourceTest::Test, "txt").
67
+ should == "Test.txt"
68
+ Resource.resource_get(RubyExt::ForResource::ResourceTest::Test, "Data.txt").
69
+ should == "Data.txt"
70
+ end
71
+
72
+ it "Class to Path" do
73
+ lambda{Resource.class_to_virtual_file("A::B::C").should =~ /A\/B\/C/}.should raise_error(/doesn't exist!/)
74
+ Resource.class_to_virtual_file("RubyExt::ForResource::ResourceTest::Test").should =~
75
+ /RubyExt\/ForResource\/ResourceTest\/Test/
76
+ end
77
+
78
+ it "change listeners" do
79
+ class CustomObserver
80
+ attr_reader :classes, :resources
81
+
82
+ def initialize
83
+ @classes, @resources = [], []
84
+ end
85
+
86
+ def update_resource type, klass, resource
87
+ if type == :class
88
+ @classes << klass
89
+ else
90
+ @classes << klass
91
+ @resources << resource
92
+ end
93
+ end
94
+ end
95
+
96
+ obs = CustomObserver.new
97
+ Resource.add_observer obs
98
+
99
+ begin
100
+ Resource.start_watching(1)
101
+
102
+ sleep 0.1
103
+ base = "#{File.dirname(__FILE__)}/RubyExt/ForResource"
104
+ File.write("#{base}/ChangedClass.rb", "class ChangedClass; end")
105
+ File.write("#{base}/ChangedClass.txt", "")
106
+ File.write("#{base}/ChangedClass.res/Text.txt", "")
107
+ sleep 1.5
108
+ ensure
109
+ Resource.stop_watching
110
+ end
111
+
112
+ obs.classes.size.should == 3
113
+ obs.classes.all?{|k| k == ForResource::ChangedClass}.should be_true
114
+
115
+ obs.resources.size.should == 2
116
+ obs.resources.should include("txt")
117
+ obs.resources.should include("Text.txt")
118
+ end
119
+
120
+ it "ResourceProvider Chaining" do
121
+ Resource.class_get("ChainTest").should == %{\
122
+ class ChainTest;
123
+ # "ProviderB"
124
+ end}
125
+ Resource.resource_get(ChainTest, "resource").should == "ProviderB"
126
+ end
127
+
128
+ it "ResourceExtension" do
129
+ require "#{File.expand_path(File.dirname(__FILE__))}/../lib/RubyExt/require"
130
+ Resource.resource_set RubyExt::ForResource::ResourceExtension, "Data.yaml", {:value => true}
131
+ res = Resource.resource_get RubyExt::ForResource::ResourceExtension, "Data.yaml"
132
+ res.should == {:value => true}
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,201 @@
1
+ require 'RubyExt/require'
2
+ require 'spec'
3
+
4
+ module RubyExt
5
+ describe "State Mashine" do
6
+ class Meta < StateMashine
7
+ initial_state :Creation
8
+ transitions [:Creation, :create, :View],
9
+ [:View, :edit, :Edit],
10
+ [:Edit, :save, :View],
11
+ [:Edit, :cancel, :View],
12
+ [:View, :delete, :Deleted]
13
+ end
14
+
15
+ class BareMeta < StateMashine
16
+ initial_state :Created
17
+ transitions [:Created, :delete, :Deleted]
18
+ end
19
+
20
+ class MetadataTest < StateMashine
21
+ transitions [:s1, :fun, :s2], [:s3, :fun, :s4]
22
+ end
23
+
24
+ before :each do
25
+ @state = Meta.new
26
+ end
27
+
28
+ it "Initial State" do
29
+ @state.should == :Creation
30
+ end
31
+
32
+ it "Exceptions in event processing should cause rollback in state change" do
33
+ @state.on_event :Creation, :create do
34
+ raise
35
+ end
36
+ lambda{@state.event :create}.should raise_error
37
+ @state.state.should == :Creation
38
+ end
39
+
40
+ it "Should properly process events inside another event (from error)" do
41
+ @state.on_event :Creation, :create do
42
+ @state.event :edit
43
+ end
44
+ @state.event :create
45
+ @state.state.should == :Edit
46
+ end
47
+
48
+ it "Transfer function" do
49
+ @state.event :create
50
+ @state.should == :View
51
+ end
52
+
53
+ it "==" do
54
+ state2 = Meta.new
55
+ @state.should == state2
56
+ @state.should == :Creation
57
+ end
58
+
59
+ it "before & after" do
60
+ @state.on_entry :View, :entry
61
+ @state.on_exit :View, :_exit
62
+
63
+ mock = mock("Events")
64
+ @state.object = mock
65
+
66
+ mock.should_receive(:entry).twice
67
+ @state.event :create
68
+ mock.should_receive(:_exit)
69
+ @state.event :edit
70
+
71
+ @state.event :cancel
72
+ @state.on_entry :View, nil
73
+ @state.on_exit :View, nil
74
+
75
+ @state.event :edit
76
+ end
77
+
78
+ it "on_event" do
79
+ mock = mock("Events")
80
+ mock.should_receive :create
81
+ @state.on_event :Creation, :create do
82
+ mock.create
83
+ end
84
+ @state.event :create
85
+ end
86
+
87
+ it "should also use block as action" do
88
+ mock = mock("Events")
89
+ @state.on_entry :View do
90
+ mock.entry
91
+ end
92
+
93
+ mock.should_receive(:entry)
94
+ @state.event :create
95
+ end
96
+
97
+ it "Invalid method" do
98
+ lambda{@state.event :invalid}.should raise_error(/There is no :invalid Event in :Creation State/)
99
+ end
100
+
101
+ it "Invalid state" do
102
+ lambda{@state.event :delete}.should raise_error(/There is no :delete Event in :Creation State!/)
103
+ end
104
+
105
+ it "Bare State" do
106
+ s = BareMeta.new
107
+ s.state = :Deleted
108
+ end
109
+
110
+ # (Waiting) - begin -> (Action) - begin -> (NestedAction) - begin n-times -> (NestedAction)
111
+ # <- end - <- end - < end n-times -
112
+ # <- cancel -
113
+ it "Custom Recursive State" do
114
+ class NestedAction
115
+ def initialize
116
+ @nesting_level = 0
117
+ end
118
+
119
+ def begin
120
+ @nesting_level += 1
121
+ :NestedAction
122
+ end
123
+
124
+ def end
125
+ if @nesting_level == 0
126
+ return :Action
127
+ else
128
+ @nesting_level -= 1
129
+ return :NestedAction
130
+ end
131
+ end
132
+
133
+ def cancel
134
+ @nesting_level = 0
135
+ return :Waiting
136
+ end
137
+
138
+ # Not required, can be omited
139
+ def state state
140
+ case state
141
+ when :Action
142
+ @nesting_level = 0
143
+ return :Action
144
+ else nil
145
+ end
146
+ end
147
+ end
148
+
149
+ class CustomState < StateMashine
150
+ initial_state :Waiting
151
+ transitions [:Waiting, :begin, :Action],
152
+ [:Action, :end, :Waiting],
153
+ [:Action, :begin, :NestedAction]
154
+ custom_state :NestedAction, NestedAction
155
+ end
156
+
157
+ sm = CustomState.new
158
+ sm.event :begin
159
+ sm.state.should == :Action
160
+ sm.event :begin
161
+ sm.state.should == :NestedAction
162
+
163
+ # Nested :begin/:end
164
+ sm.event :begin
165
+ sm.state.should == :NestedAction
166
+ sm.event :begin
167
+ sm.state.should == :NestedAction
168
+
169
+ sm.event :end
170
+ sm.state.should == :NestedAction
171
+ sm.event :end
172
+ sm.state.should == :NestedAction
173
+ lambda{sm.event :invalid}.should raise_error(/There is no :invalid Event in :NestedAction State/)
174
+
175
+ sm.event :end
176
+ sm.state.should == :Action
177
+
178
+ # Nested :cancel
179
+ sm.event :begin
180
+ sm.state.should == :NestedAction
181
+ sm.event :begin
182
+ sm.state.should == :NestedAction
183
+ sm.event :cancel
184
+ sm.state.should == :Waiting
185
+
186
+ # Correct working after :cancel
187
+ sm.event :begin
188
+ sm.state.should == :Action
189
+ sm.event :begin
190
+ sm.state.should == :NestedAction
191
+ sm.event :end
192
+ sm.state.should == :Action
193
+
194
+ # Should also works with "state= ..."
195
+ sm.event :begin
196
+ sm.state.should == :NestedAction
197
+ sm.state = :Action
198
+ sm.state.should == :Action
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,66 @@
1
+ require 'RubyExt/require'
2
+ require 'spec'
3
+
4
+ module RubyExt
5
+ describe "Synchronize" do
6
+ it "synchronize" do
7
+ class Account
8
+ inherit RubyExt::Synchronizer
9
+ attr_reader :from, :to
10
+
11
+ def initialize
12
+ super
13
+ @from, @to = 0, 0
14
+ end
15
+
16
+ def transfer
17
+ @from -= 1
18
+ @to += 1
19
+ end
20
+ synchronize :transfer
21
+ end
22
+
23
+ a, threads = Account.new, []
24
+ 100.times do
25
+ t = Thread.new do
26
+ 100.times{a.transfer}
27
+ end
28
+ threads << t
29
+ end
30
+ threads.each{|t| t.join}
31
+
32
+ a.from.should == -10_000
33
+ a.to.should == 10_000
34
+ end
35
+
36
+ it "synchronize_all" do
37
+ class Account
38
+ inherit RubyExt::Synchronizer
39
+ attr_reader :from, :to
40
+
41
+ def initialize
42
+ super
43
+ @from, @to = 0, 0
44
+ end
45
+
46
+ def transfer
47
+ @from -= 1
48
+ @to += 1
49
+ end
50
+ synchronize_all
51
+ end
52
+
53
+ a, threads = Account.new, []
54
+ 100.times do
55
+ t = Thread.new do
56
+ 100.times{a.transfer}
57
+ end
58
+ threads << t
59
+ end
60
+ threads.each{|t| t.join}
61
+
62
+ a.from.should == -10_000
63
+ a.to.should == 10_000
64
+ end
65
+ end
66
+ end