RubyExt 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.
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