gexp 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,33 @@
|
|
1
|
+
module Gexp
|
2
|
+
class Receiver
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def current=(receiver)
|
7
|
+
@current = receiver
|
8
|
+
end
|
9
|
+
|
10
|
+
def current
|
11
|
+
@receiver ||= self.new
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(user, request)
|
17
|
+
@user = user
|
18
|
+
@request = request
|
19
|
+
|
20
|
+
@stack = Gexp::Command::Stack.new \
|
21
|
+
@user, @request
|
22
|
+
end
|
23
|
+
|
24
|
+
def receive
|
25
|
+
@stack.map do |command|
|
26
|
+
Gexp::Mongoid::Transaction.with do |context|
|
27
|
+
command.perform
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Gexp
|
2
|
+
module StateDefinition
|
3
|
+
module StateMachine
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def define_state_by(state_configs)
|
13
|
+
sm = state_configs
|
14
|
+
state_aliases = [ :all, :any ]
|
15
|
+
|
16
|
+
self.instance_eval do
|
17
|
+
state_machine initial: sm[:initial] do
|
18
|
+
|
19
|
+
# Внешний хук от Handlers
|
20
|
+
around_transition :around_handlers
|
21
|
+
|
22
|
+
# State Machine Context
|
23
|
+
smc = self
|
24
|
+
|
25
|
+
# State definition
|
26
|
+
sm[:states].each do |name, methods|
|
27
|
+
smc.state name do
|
28
|
+
(methods || []).each do |method|
|
29
|
+
self.send(method)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Events definition
|
35
|
+
sm[:events].each do |name, transitions|
|
36
|
+
|
37
|
+
smc.event name do
|
38
|
+
|
39
|
+
transitions.each do |opts|
|
40
|
+
opts[:if] ||= []
|
41
|
+
opts[:do] ||= []
|
42
|
+
|
43
|
+
from = opts[:from] || :any
|
44
|
+
to = opts[:to] || :any
|
45
|
+
|
46
|
+
from = send(from) if state_aliases.include?(from)
|
47
|
+
to = send(to) if state_aliases.include?(to)
|
48
|
+
|
49
|
+
transition from => to, :if => lambda { |item|
|
50
|
+
opts[:if].map { |method|
|
51
|
+
item.send(method)
|
52
|
+
}.all?
|
53
|
+
}
|
54
|
+
#, :do => lambda { |item|
|
55
|
+
# opts[:do].each { |method|
|
56
|
+
# item.send(method)
|
57
|
+
# }
|
58
|
+
#}
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Before/After transition
|
65
|
+
[ :before, :after ].each do |name|
|
66
|
+
|
67
|
+
(sm[name] || []).each do |opts|
|
68
|
+
|
69
|
+
from = opts[:from] || :any
|
70
|
+
to = opts[:to] || :any
|
71
|
+
|
72
|
+
from = send(from) if state_aliases.include?(from)
|
73
|
+
to = send(to) if state_aliases.include?(to)
|
74
|
+
|
75
|
+
smc.send(:"#{name}_transition", from => to, :do => lambda { |item|
|
76
|
+
opts[:do].each do |method|
|
77
|
+
item.send(method)
|
78
|
+
end
|
79
|
+
})
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/gexp/version.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Command::Object do
|
5
|
+
|
6
|
+
context "Команда pick на объекте у себя в локации" do
|
7
|
+
|
8
|
+
before do
|
9
|
+
@user = Fabricate(:user)
|
10
|
+
@request = HashWithIndifferentAccess.new({
|
11
|
+
:params => {
|
12
|
+
:sended_at => 123456789.012,
|
13
|
+
:create_at => 123456789.012,
|
14
|
+
:commands => [{
|
15
|
+
# У себя на карте
|
16
|
+
:event => :pick,
|
17
|
+
:stage => { x: 100, y: 200 },
|
18
|
+
:rewards => { energy: -1, exp: 5 },
|
19
|
+
:timestamp => 123456789.012,
|
20
|
+
:object => { 'item.stage.house' => '55a55' },
|
21
|
+
:transition => { :builded => :builded },
|
22
|
+
:seed => 532434234,
|
23
|
+
}]
|
24
|
+
}
|
25
|
+
})
|
26
|
+
|
27
|
+
@object = Object.new
|
28
|
+
@context = Object.new
|
29
|
+
|
30
|
+
stub(Item::Stage::House).find.with('55a55') { @object }
|
31
|
+
|
32
|
+
lambda {
|
33
|
+
@command = Gexp::Command::Object.new @request[:params][:commands].first
|
34
|
+
@command.context = @context
|
35
|
+
}.should_not raise_error
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
it "Команда должна загружать объекты" do
|
40
|
+
@command.object.should == @object
|
41
|
+
end
|
42
|
+
|
43
|
+
it "Для конмады определенно событие" do
|
44
|
+
@command.event.should == :pick
|
45
|
+
end
|
46
|
+
|
47
|
+
it "Нельзя (так просто взять и) создать команду без параметра-объекта" do
|
48
|
+
params = @request[:params][:commands].first
|
49
|
+
params.delete(:object)
|
50
|
+
lambda {
|
51
|
+
@command = Gexp::Command::Object.new params
|
52
|
+
}.should raise_error Regexp.new("Can't find object")
|
53
|
+
end
|
54
|
+
|
55
|
+
context "#perform" do
|
56
|
+
|
57
|
+
it "При успешном выполнении должен вызывать соотвествующее соьытие у объекта" do
|
58
|
+
mock(@object).pick.once { true }
|
59
|
+
@command.perform
|
60
|
+
@command.should be_done
|
61
|
+
end
|
62
|
+
|
63
|
+
context "При неуспешном выполнении" do
|
64
|
+
|
65
|
+
before do
|
66
|
+
mock(@object).pick.once { raise 'Something wrong' }
|
67
|
+
end
|
68
|
+
|
69
|
+
it "Команда должна находится в статусе failed" do
|
70
|
+
@command.perform
|
71
|
+
@command.should be_failed
|
72
|
+
end
|
73
|
+
|
74
|
+
it "исключение должно агрегироваться в соотвествующем поле" do
|
75
|
+
@command.perform
|
76
|
+
@command.errors.should_not be_empty
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Command::Stack do
|
5
|
+
|
6
|
+
context "#each" do
|
7
|
+
|
8
|
+
let(:request) {
|
9
|
+
HashWithIndifferentAccess.new({
|
10
|
+
:params => {
|
11
|
+
:sended_at => 123456789.012,
|
12
|
+
:create_at => 123456789.012,
|
13
|
+
:commands => [
|
14
|
+
{ # У себя на карте
|
15
|
+
:event => :pick,
|
16
|
+
:stage => { x: 100, y: 200 },
|
17
|
+
:rewards => { energy: -1, exp: 5 },
|
18
|
+
:timestamp => 123456789.012,
|
19
|
+
:objects => [{ 'item.stage.house' => '50c12939584aa5488a000001' }],
|
20
|
+
:transition => { :builded => :builded },
|
21
|
+
:timestamp => 123456789.010,
|
22
|
+
:seed => 532434234,
|
23
|
+
}, { # У друга на карте
|
24
|
+
:event => :pick,
|
25
|
+
:stage => { x: 100, y: 200 },
|
26
|
+
:uid => '<friend_uid>',
|
27
|
+
:rewards => { energy: -1, exp: 5 },
|
28
|
+
:timestamp => 123456789.012,
|
29
|
+
:objects => [{ 'item.stage.house' => '50c12939584aa5488a000001' }],
|
30
|
+
:transition => { :builded => :builded },
|
31
|
+
:timestamp => 123456789.011,
|
32
|
+
:seed => 532434234,
|
33
|
+
}, { # создание здания
|
34
|
+
:event => :create,
|
35
|
+
:stage => { x: 100, y: 200 },
|
36
|
+
:objects => [{ 'item.stage.house' => '50c12939584aa5488a000001' }],
|
37
|
+
:reward => { gold: -100 },
|
38
|
+
:timestamp => 123456789.012,
|
39
|
+
:seed => 532434234,
|
40
|
+
}, { # создание контракта
|
41
|
+
:event => :pick,
|
42
|
+
:stage => { x: 100, y: 200 },
|
43
|
+
:objects => [{ 'item.stage.house' => '50c12939584aa5488a000001' }],
|
44
|
+
:timestamp => 123456789.013,
|
45
|
+
:seed => 532412334,
|
46
|
+
},
|
47
|
+
]
|
48
|
+
}
|
49
|
+
})
|
50
|
+
}
|
51
|
+
|
52
|
+
before do
|
53
|
+
@user = Fabricate(:user)
|
54
|
+
@request = request
|
55
|
+
end
|
56
|
+
|
57
|
+
it "Получаем команды из фабрики" do
|
58
|
+
@stack = Gexp::Command::Stack.new \
|
59
|
+
@user, @request
|
60
|
+
@stack.size.should == @request[:params][:commands].size
|
61
|
+
end
|
62
|
+
|
63
|
+
it "Выбрасываем исключение если комманда уже есть" do
|
64
|
+
dup_command = @request[:params][:commands].last.dup
|
65
|
+
@request[:params][:commands] << dup_command
|
66
|
+
lambda {
|
67
|
+
@stack = Gexp::Command::Stack.new \
|
68
|
+
@user, @request
|
69
|
+
}.should raise_error /Command with hash .+ be exist/
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Command do
|
5
|
+
|
6
|
+
context "Базовый функционал команды" do
|
7
|
+
|
8
|
+
before do
|
9
|
+
@user = Fabricate(:user)
|
10
|
+
@request = HashWithIndifferentAccess.new({
|
11
|
+
:params => {
|
12
|
+
:event => :command_event,
|
13
|
+
:timestamp => 1234567890.123,
|
14
|
+
:seed => 123321123 }
|
15
|
+
})
|
16
|
+
|
17
|
+
@user = Object.new
|
18
|
+
@context = Object.new
|
19
|
+
|
20
|
+
lambda {
|
21
|
+
@command = Gexp::Command.new @request[:params]
|
22
|
+
@command.context = @context
|
23
|
+
}.should_not raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
it "Команде должен выставлятся контекст" do
|
28
|
+
@command.context.should == @context
|
29
|
+
end
|
30
|
+
|
31
|
+
it "В команду должны должны копироваться параметры" do
|
32
|
+
@command.params.should == @request[:params]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "Команда должны реализовывать StateMachine (положительный сценарий)" do
|
36
|
+
@command.should be_new
|
37
|
+
@command.activate!
|
38
|
+
@command.should be_active
|
39
|
+
@command.complete!
|
40
|
+
@command.should be_done
|
41
|
+
end
|
42
|
+
|
43
|
+
it "Команда должны реализовывать StateMachine (отрицательный сценарий)" do
|
44
|
+
@command.should be_new
|
45
|
+
@command.activate!
|
46
|
+
@command.should be_active
|
47
|
+
@command.failure!
|
48
|
+
@command.should be_failed
|
49
|
+
end
|
50
|
+
|
51
|
+
it "Базовую команду нельзя выполнить" do
|
52
|
+
lambda {
|
53
|
+
@command.perform
|
54
|
+
}.should raise_error /Not defined perform method/
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Handler::Check::Item do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@user = User.new
|
8
|
+
@object = Object.new
|
9
|
+
|
10
|
+
subject.user = @user
|
11
|
+
subject.object = @object
|
12
|
+
end
|
13
|
+
|
14
|
+
it "Если имеем объект и не передали блок" do
|
15
|
+
mock(@user).have_with_type?('item.stage.house', 1) { true }
|
16
|
+
result = subject.process({ 'item.stage.house' => 1 })
|
17
|
+
result.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "Если имеем объект передали блок" do
|
21
|
+
mock(@user).have_with_type?('item.stage.house', 1) { true }
|
22
|
+
result = false
|
23
|
+
subject.process({ 'item.stage.house' => 1 }) {
|
24
|
+
result = true
|
25
|
+
}.should be_true
|
26
|
+
result.should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "Если не имеем объект и не передали блок" do
|
30
|
+
mock(@user).have_with_type?('item.stage.house', 1) { false }
|
31
|
+
lambda {
|
32
|
+
subject.process({ 'item.stage.house' => 1 })
|
33
|
+
}.should raise_error /not_have_items/
|
34
|
+
end
|
35
|
+
|
36
|
+
it "Если имеем объект передали блок" do
|
37
|
+
mock(@user).have_with_type?('item.stage.house', 1) { false }
|
38
|
+
result = false
|
39
|
+
subject.process({ 'item.stage.house' => 1 }) {
|
40
|
+
result = true
|
41
|
+
}.should be_nil
|
42
|
+
result.should be_false
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb')
|
3
|
+
|
4
|
+
describe Gexp::Handler::Check::Resources do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@user = User.new
|
8
|
+
@object = Object.new
|
9
|
+
|
10
|
+
subject.user = @user
|
11
|
+
subject.object = @object
|
12
|
+
end
|
13
|
+
|
14
|
+
context "#access_resource" do
|
15
|
+
|
16
|
+
before {
|
17
|
+
pending "Еще не проверялись"
|
18
|
+
}
|
19
|
+
|
20
|
+
it "Возвращение значения ресурса" do
|
21
|
+
@user.energy = 5
|
22
|
+
energy = subject.chain_resource('energy', @user)
|
23
|
+
energy.should == 5
|
24
|
+
end
|
25
|
+
|
26
|
+
it "Возвращение значения вложенного ресурса" do
|
27
|
+
@user.resources.resource_1 = 5
|
28
|
+
resource_1 = subject.chain_resource('resources.resource_1', @user)
|
29
|
+
resource_1.resource_1.should == 5
|
30
|
+
end
|
31
|
+
|
32
|
+
it "Назначение значения ресурса" do
|
33
|
+
@user.energy = 5
|
34
|
+
subject.chain_resource('energy=', @user, 6)
|
35
|
+
@user.energy.should == 6
|
36
|
+
end
|
37
|
+
|
38
|
+
it "Назначение значения ыложенного ресурса" do
|
39
|
+
@user.resources.resource_1 = 5
|
40
|
+
subject.chain_resource('resources.resource_1=', @user, 6)
|
41
|
+
@user.resources.resource_1.should == 6
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
it "Если у пользователя есть ресурс" do
|
47
|
+
@user.energy = 5
|
48
|
+
subject.process(energy: 5).should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "Если у пользователя есть ресурсы" do
|
52
|
+
@user.energy = 5
|
53
|
+
@user.exp = 5
|
54
|
+
subject.process(energy: 5, exp: 3).should be_true
|
55
|
+
end
|
56
|
+
|
57
|
+
it "Если у пользователя нет ресурсов" do
|
58
|
+
@user.energy = 5
|
59
|
+
@user.exp = 5
|
60
|
+
lambda {
|
61
|
+
subject.process(energy: 6, exp: 7).should be_false
|
62
|
+
}.should raise_error /out_of_resources\-energy,exp/
|
63
|
+
end
|
64
|
+
|
65
|
+
it "Успешная проверка ресурсов с блоком" do
|
66
|
+
@user.energy = 5
|
67
|
+
@user.exp = 5
|
68
|
+
prob = false
|
69
|
+
subject.process(energy: 5, exp: 3) { prob = true }
|
70
|
+
prob.should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "Не успешная проверка ресурсов с блоком" do
|
74
|
+
@user.energy = 0
|
75
|
+
@user.exp = 5
|
76
|
+
prob = false
|
77
|
+
subject.process(energy: 5, exp: 3) { prob = true }
|
78
|
+
prob.should be_false
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|