gexp 0.0.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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/gexp.gemspec +32 -0
- data/lib/gexp.rb +16 -0
- data/lib/gexp/command.rb +50 -0
- data/lib/gexp/command/object.rb +37 -0
- data/lib/gexp/command/stack.rb +50 -0
- data/lib/gexp/handler.rb +25 -0
- data/lib/gexp/handler/caller.rb +8 -0
- data/lib/gexp/handler/check.rb +9 -0
- data/lib/gexp/handler/check/item.rb +34 -0
- data/lib/gexp/handler/check/resources.rb +56 -0
- data/lib/gexp/handler/modify.rb +9 -0
- data/lib/gexp/handler/modify/resources.rb +81 -0
- data/lib/gexp/handler/producer.rb +61 -0
- data/lib/gexp/handler/transition.rb +44 -0
- data/lib/gexp/handler/transition/builder.rb +33 -0
- data/lib/gexp/mongoid.rb +4 -0
- data/lib/gexp/mongoid/transaction.rb +266 -0
- data/lib/gexp/object.rb +39 -0
- data/lib/gexp/receiver.rb +33 -0
- data/lib/gexp/state_definition.rb +4 -0
- data/lib/gexp/state_definition/state_machine.rb +90 -0
- data/lib/gexp/version.rb +3 -0
- data/spec/gexp/command/object_spec.rb +84 -0
- data/spec/gexp/command/stack_spec.rb +74 -0
- data/spec/gexp/command_spec.rb +59 -0
- data/spec/gexp/handler/check/item_spec.rb +45 -0
- data/spec/gexp/handler/check/resources_spec.rb +81 -0
- data/spec/gexp/handler/check_spec.rb +5 -0
- data/spec/gexp/handler/modify/resources_spec.rb +96 -0
- data/spec/gexp/handler/modify_spec.rb +6 -0
- data/spec/gexp/handler/producer_spec.rb +85 -0
- data/spec/gexp/handler/transition/builder_spec.rb +122 -0
- data/spec/gexp/handler/transition_spec.rb +14 -0
- data/spec/gexp/mongoid/transaction_spec.rb +210 -0
- data/spec/gexp/state_definition/state_machine_spec.rb +48 -0
- data/spec/spec_helper.rb +10 -0
- metadata +210 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Handler::Modify::Resources do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@user = User.new
|
8
|
+
@object = Item.new
|
9
|
+
|
10
|
+
subject.user = @user
|
11
|
+
subject.object = @object
|
12
|
+
end
|
13
|
+
|
14
|
+
context "#normilize" do
|
15
|
+
|
16
|
+
it "с положительным параметром" do
|
17
|
+
value = 100500
|
18
|
+
subject.normalize(:undefined, value).should == value
|
19
|
+
end
|
20
|
+
|
21
|
+
it "с отрицательным параметром" do
|
22
|
+
value = -3
|
23
|
+
subject.normalize(:undefined, value).should be_zero
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
context "#normalize_energy" do
|
29
|
+
|
30
|
+
before { @max_energy = 20 }
|
31
|
+
|
32
|
+
it "с отрицательным значением" do
|
33
|
+
value = -1
|
34
|
+
subject.normalize(:energy, value).should be_zero
|
35
|
+
end
|
36
|
+
|
37
|
+
context "Если не привышаем лимит" do
|
38
|
+
before { stub(@user).max_energy.once { @max_energy } }
|
39
|
+
|
40
|
+
it "с нормальным значением" do
|
41
|
+
value = @max_energy / 2
|
42
|
+
subject.normalize(:energy, value).should == value
|
43
|
+
end
|
44
|
+
|
45
|
+
it "с максимальным значением" do
|
46
|
+
value = @max_energy
|
47
|
+
subject.normalize(:energy, value).should == value
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
it "со сверх большим значением" do
|
53
|
+
stub(@user).max_energy.twice { @max_energy }
|
54
|
+
value = @max_energy + 1
|
55
|
+
subject.normalize(:energy, value).should == @max_energy
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
context "Изменение ресурсов" do
|
61
|
+
|
62
|
+
it "Энергия должна быть начисленна" do
|
63
|
+
@user.energy = 5
|
64
|
+
subject.process({ energy: 5 })
|
65
|
+
@user.energy.should == 10
|
66
|
+
end
|
67
|
+
|
68
|
+
it "Энергия должна списываться" do
|
69
|
+
@user.energy = 10
|
70
|
+
subject.process({ energy: -5 })
|
71
|
+
@user.energy.should == 5
|
72
|
+
end
|
73
|
+
|
74
|
+
it "Энергия должна списываться, а экспа начисляться" do
|
75
|
+
@user.energy = 10
|
76
|
+
@user.exp = 10
|
77
|
+
subject.process({ energy: -5, exp: 5 })
|
78
|
+
@user.energy.should == 5
|
79
|
+
@user.exp.should == 15
|
80
|
+
end
|
81
|
+
|
82
|
+
it "Если ресурса не хватает, полностью прерываем транзакцию" do
|
83
|
+
@user.energy = 10
|
84
|
+
@user.exp = 10
|
85
|
+
|
86
|
+
lambda {
|
87
|
+
subject.process({ exp: 5, energy: -11 })
|
88
|
+
}.should raise_error /out_of_resource\-energy/
|
89
|
+
|
90
|
+
@user.energy.should == 10
|
91
|
+
@user.exp.should == 10
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Handler::Producer do
|
5
|
+
|
6
|
+
let(:objects) do
|
7
|
+
{
|
8
|
+
object: Object.new,
|
9
|
+
provider: Object.new,
|
10
|
+
subject: Object.new,
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:emiter) do
|
15
|
+
emiter = Gexp::Handler::Producer.new \
|
16
|
+
params,
|
17
|
+
type,
|
18
|
+
objects
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:handler) do
|
22
|
+
handler = emiter.emit
|
23
|
+
end
|
24
|
+
|
25
|
+
before do
|
26
|
+
emited_class.class.should be_a_kind_of Class
|
27
|
+
end
|
28
|
+
|
29
|
+
context "Caller" do
|
30
|
+
|
31
|
+
let(:params) do
|
32
|
+
[ :object, [ :arg1, :arg2, :arg3 ] ]
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:emited_class) do
|
36
|
+
Gexp::Handler::Caller
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:type) do
|
40
|
+
end
|
41
|
+
|
42
|
+
it "build caller for self" do
|
43
|
+
handler.should be_a_kind_of emited_class
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
context "Other Handler" do
|
49
|
+
|
50
|
+
let(:params) do
|
51
|
+
[ :other, :object, { param: :value, other_param: :over_value } ]
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:emited_class) do
|
55
|
+
Gexp::Handler.const_get(handler_class).const_set :Other, (Class.new do
|
56
|
+
def initialize(*args)
|
57
|
+
end
|
58
|
+
end)
|
59
|
+
end
|
60
|
+
|
61
|
+
context "Checker" do
|
62
|
+
|
63
|
+
let(:handler_class) { :Check }
|
64
|
+
let(:type) { :chekers }
|
65
|
+
|
66
|
+
it "build caller for self" do
|
67
|
+
handler.should be_a_kind_of emited_class
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
context "Modifier" do
|
73
|
+
|
74
|
+
let(:handler_class) { :Modify }
|
75
|
+
let(:type) { :modifiers }
|
76
|
+
|
77
|
+
it "build caller for self" do
|
78
|
+
handler.should be_a_kind_of emited_class
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Handler::Transition::Builder do
|
5
|
+
|
6
|
+
shared_examples_for Gexp::Handler::Transition::Builder do
|
7
|
+
|
8
|
+
# let(:to) { to }
|
9
|
+
|
10
|
+
# let(:from) { from }
|
11
|
+
|
12
|
+
# let(:event) { event }
|
13
|
+
|
14
|
+
let(:transition) do
|
15
|
+
transition = Object.new
|
16
|
+
stub(transition).to_name { to }
|
17
|
+
stub(transition).from_name { from }
|
18
|
+
stub(transition).event { event }
|
19
|
+
|
20
|
+
transition
|
21
|
+
end
|
22
|
+
|
23
|
+
# let(:config) do
|
24
|
+
# config
|
25
|
+
# end
|
26
|
+
|
27
|
+
# let(:result_chekers) do
|
28
|
+
# result_chekers
|
29
|
+
# end
|
30
|
+
|
31
|
+
# let(:result_modifiers) do
|
32
|
+
# result_modifiers
|
33
|
+
# end
|
34
|
+
|
35
|
+
let(:object_param) do
|
36
|
+
object = Object.new
|
37
|
+
stub(object).config { config }
|
38
|
+
object
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:subject) do
|
42
|
+
Gexp::Handler::Transition::Builder.new \
|
43
|
+
transition,
|
44
|
+
object_param,
|
45
|
+
subject_param,
|
46
|
+
provider_param
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should must be given result checkers" do
|
50
|
+
subject.checkers.should == result_chekers
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should must be given result modifiers" do
|
54
|
+
subject.modifiers.should == result_modifiers
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
context "Define handler example" do
|
60
|
+
|
61
|
+
it_behaves_like Gexp::Handler::Transition::Builder do
|
62
|
+
let(:to) { :prebuilded }
|
63
|
+
let(:from) { :created }
|
64
|
+
let(:event) { :place }
|
65
|
+
let(:config) do
|
66
|
+
{
|
67
|
+
events: {
|
68
|
+
place: {
|
69
|
+
check: [
|
70
|
+
[ :resources, :object, { wood: 5, energy: 1 } ],
|
71
|
+
[ :object, [ :place_allowed?, :not_blocked? ] ],
|
72
|
+
],
|
73
|
+
modify: [
|
74
|
+
[ :object, [ :change_tile_type! ] ]
|
75
|
+
]
|
76
|
+
},
|
77
|
+
sell: {
|
78
|
+
check: [
|
79
|
+
[ :object, [ :has_current_user? ] ]
|
80
|
+
]
|
81
|
+
}
|
82
|
+
},
|
83
|
+
transitions: {
|
84
|
+
created: {
|
85
|
+
selled: { },
|
86
|
+
prebuilded: {
|
87
|
+
check: [
|
88
|
+
[ :shared_resources, :object, { 0 => 5 } ]
|
89
|
+
],
|
90
|
+
modify: [
|
91
|
+
[ :shared_resources, :object, { 0 => 5 } ],
|
92
|
+
[ :resources, :object, { wood: -5, energy: -1 } ],
|
93
|
+
[ :shared_resources, :object, { 0 => -5 } ]
|
94
|
+
]
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
let(:result_chekers) {
|
102
|
+
[
|
103
|
+
[ :resources, :object, { wood: 5, energy: 1 } ],
|
104
|
+
[ :object, [ :place_allowed?, :not_blocked? ] ],
|
105
|
+
[ :shared_resources, :object, { 0 => 5 } ],
|
106
|
+
]
|
107
|
+
}
|
108
|
+
let(:result_modifiers) {
|
109
|
+
[
|
110
|
+
[ :object, [ :change_tile_type! ] ],
|
111
|
+
[ :shared_resources, :object, { 0 => 5 } ],
|
112
|
+
[ :resources, :object, { wood: -5, energy: -1 } ],
|
113
|
+
[ :shared_resources, :object, { 0 => -5 } ]
|
114
|
+
]
|
115
|
+
}
|
116
|
+
let(:subject_param) { Object.new }
|
117
|
+
let(:provider_param) { Object.new }
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Handler::Transition do
|
5
|
+
|
6
|
+
shared_examples_for "construct handler" do
|
7
|
+
|
8
|
+
let(:object) { Object.new }
|
9
|
+
let(:subject) { Object.new }
|
10
|
+
let(:provider) {}
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Mongoid::Transaction do
|
5
|
+
|
6
|
+
context "Transactional Object" do
|
7
|
+
|
8
|
+
before do
|
9
|
+
|
10
|
+
class Foo < Gexp::Mongoid::Transaction::Base
|
11
|
+
field :name, type: String
|
12
|
+
field :quantity, type: Integer
|
13
|
+
end
|
14
|
+
|
15
|
+
class Baz < Gexp::Mongoid::Transaction::Base
|
16
|
+
field :name, type: String
|
17
|
+
field :quantity, type: Integer
|
18
|
+
end
|
19
|
+
|
20
|
+
@foo = Foo.create(name: 'foo', quantity: 0)
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
it "Проверка полей Foo#*" # TODO: нужены mongoid-rspec матчеры
|
25
|
+
|
26
|
+
context "Жизненный цикл транзакции" do
|
27
|
+
|
28
|
+
def inner_assets_for(foo, transaction)
|
29
|
+
foo.name = 'new_foo'
|
30
|
+
foo.name.should == 'new_foo'
|
31
|
+
foo._updated['name'].should == 'new_foo'
|
32
|
+
foo.attributes['name'].should == 'foo'
|
33
|
+
foo.transaction.should == transaction
|
34
|
+
foo._version.should be_zero
|
35
|
+
|
36
|
+
foo.should be_dirty_uncommited
|
37
|
+
foo.save
|
38
|
+
end
|
39
|
+
|
40
|
+
def outer_assets_for(foo)
|
41
|
+
foo.reload
|
42
|
+
foo.name.should == 'new_foo'
|
43
|
+
foo._transaction_id.should be_nil
|
44
|
+
foo._updated.should be_nil
|
45
|
+
foo._version.should_not be_zero
|
46
|
+
foo.should be_clear
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
it "Изменения без транзакции" do
|
51
|
+
@foo.reload
|
52
|
+
@foo.name = 'new_foo'
|
53
|
+
@foo.name.should == 'new_foo'
|
54
|
+
@foo._updated.should be_nil
|
55
|
+
@foo.transaction.should be_nil
|
56
|
+
@foo.save
|
57
|
+
@foo.reload
|
58
|
+
@foo.name.should == 'new_foo'
|
59
|
+
end
|
60
|
+
|
61
|
+
it "Транзакция на внутреннем новом объекте" do
|
62
|
+
foo = nil
|
63
|
+
|
64
|
+
Gexp::Mongoid::Transaction.with do |context|
|
65
|
+
foo = Foo.create(name: 'foo', quantity: 0)
|
66
|
+
inner_assets_for foo, context.transaction
|
67
|
+
end
|
68
|
+
|
69
|
+
outer_assets_for foo
|
70
|
+
end
|
71
|
+
|
72
|
+
it "Транзакция на внутреннем загруженном объекте" do
|
73
|
+
|
74
|
+
exist = Foo.create(name: 'foo', quantity: 0)
|
75
|
+
foo = nil
|
76
|
+
Gexp::Mongoid::Transaction.with do |context|
|
77
|
+
foo = Foo.find(exist)
|
78
|
+
inner_assets_for foo, context.transaction
|
79
|
+
end
|
80
|
+
|
81
|
+
outer_assets_for foo
|
82
|
+
end
|
83
|
+
|
84
|
+
it "Транзакция на внешнем новом объекте" do
|
85
|
+
foo = Foo.create(name: 'foo', quantity: 0)
|
86
|
+
Gexp::Mongoid::Transaction.with do |context|
|
87
|
+
inner_assets_for foo, context.transaction
|
88
|
+
end
|
89
|
+
|
90
|
+
outer_assets_for foo
|
91
|
+
end
|
92
|
+
|
93
|
+
it "Транзакция на внешнем загруженном объекте" do
|
94
|
+
exist = Foo.create(name: 'foo', quantity: 0)
|
95
|
+
foo = Foo.create(name: 'foo', quantity: 0)
|
96
|
+
|
97
|
+
Gexp::Mongoid::Transaction.with(foo) do |context|
|
98
|
+
foo.transaction.should == context.transaction
|
99
|
+
context.transaction.objects.include?(foo).should == true
|
100
|
+
inner_assets_for foo, context.transaction
|
101
|
+
end
|
102
|
+
|
103
|
+
outer_assets_for foo
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
context "Проверка признаков объекта" do
|
109
|
+
|
110
|
+
it "Чистый объект" do
|
111
|
+
@foo.should be_clear
|
112
|
+
@foo.should_not be_dirty_commited
|
113
|
+
@foo.should_not be_dirty_uncommited
|
114
|
+
end
|
115
|
+
|
116
|
+
it "Грязный закомиченный объект" do
|
117
|
+
@foo.create_update
|
118
|
+
@foo._transaction_id = '12312312asd3123'
|
119
|
+
|
120
|
+
@foo.should_not be_clear
|
121
|
+
@foo.should be_dirty_commited
|
122
|
+
@foo.should_not be_dirty_uncommited
|
123
|
+
end
|
124
|
+
|
125
|
+
it "Грязный незакомиченынй объект" do
|
126
|
+
@foo.create_update
|
127
|
+
@foo.transaction = Gexp::Mongoid::Transaction::Instance.create
|
128
|
+
@foo.should_not be_clear
|
129
|
+
@foo.should_not be_dirty_commited
|
130
|
+
@foo.should be_dirty_uncommited
|
131
|
+
|
132
|
+
Gexp::Mongoid::Transaction::Observer.finish_transaction!
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
context "Загрузка и запись" do
|
138
|
+
|
139
|
+
before do
|
140
|
+
@foo = Foo.create(name: 'name', quantity: 0)
|
141
|
+
@baz = Baz.create(name: 'name', quantity: 0)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "чистого объекта" do
|
145
|
+
loaded = Foo.find(@foo.id)
|
146
|
+
loaded.should be_clear
|
147
|
+
end
|
148
|
+
|
149
|
+
it "В нормальном режиме" do
|
150
|
+
Gexp::Mongoid::Transaction.with do |context|
|
151
|
+
foo = Foo.find(@foo.id)
|
152
|
+
foo.name = 'new_name_foo'
|
153
|
+
|
154
|
+
baz = Baz.find(@baz.id)
|
155
|
+
baz.name = 'new_name_baz'
|
156
|
+
end
|
157
|
+
|
158
|
+
@foo.reload
|
159
|
+
@foo.name.should == 'new_name_foo'
|
160
|
+
@foo._version.should_not be_zero
|
161
|
+
@foo.should be_clear
|
162
|
+
|
163
|
+
@baz.reload
|
164
|
+
@baz.name.should == 'new_name_baz'
|
165
|
+
@baz._version.should_not be_zero
|
166
|
+
@baz.should be_clear
|
167
|
+
end
|
168
|
+
|
169
|
+
it "грязного не закоммиченного объекта с проваленной транзакцией" do
|
170
|
+
obj = @foo
|
171
|
+
lambda {
|
172
|
+
Gexp::Mongoid::Transaction.with do |context|
|
173
|
+
obj.name = 'new_name'
|
174
|
+
obj.should be_dirty_uncommited
|
175
|
+
obj.save
|
176
|
+
obj.reload
|
177
|
+
obj.name.should == 'name'
|
178
|
+
raise 'Something wrong'
|
179
|
+
end
|
180
|
+
}.should raise_error /Something wrong/
|
181
|
+
|
182
|
+
loaded = Foo.find(obj.id)
|
183
|
+
loaded.should be_clear
|
184
|
+
|
185
|
+
loaded.name.should == 'name'
|
186
|
+
end
|
187
|
+
|
188
|
+
it "грязного не закоммиченного объекта при проваленом коммите транзакции" do
|
189
|
+
lambda {
|
190
|
+
Gexp::Mongoid::Transaction.with do |context|
|
191
|
+
stub(context).end_transaction { raise 'Something wrong' }
|
192
|
+
@foo.name = 'new_name'
|
193
|
+
@foo.save
|
194
|
+
end
|
195
|
+
}.should raise_error /Something wrong/
|
196
|
+
|
197
|
+
@foo.should be_dirty
|
198
|
+
@foo.should be_dirty_commited
|
199
|
+
#mock(@foo).clear_commited!.once
|
200
|
+
mock(@foo).clear_uncommited!.never
|
201
|
+
@foo.reload
|
202
|
+
@foo.name.should == 'new_name'
|
203
|
+
@foo.should be_clear
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|