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,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
|