gexp 0.0.3 → 0.0.4
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/docs/commands_handling.mdoc +72 -0
- data/docs/notes.mdoc +19 -0
- data/gexp.gemspec +1 -0
- data/lib/examples/item_example.rb +32 -52
- data/lib/examples/user_example.rb +7 -3
- data/lib/gexp/command.rb +17 -1
- data/lib/gexp/command/object.rb +20 -5
- data/lib/gexp/command/stack.rb +15 -1
- data/lib/gexp/handler.rb +8 -8
- data/lib/gexp/handler/modify/resources.rb +1 -0
- data/lib/gexp/handler/producer.rb +31 -17
- data/lib/gexp/handler/transition.rb +39 -11
- data/lib/gexp/handler/transition/builder.rb +33 -7
- data/lib/gexp/item.rb +73 -0
- data/lib/gexp/object.rb +8 -0
- data/lib/gexp/receiver/example.rb +2 -2
- data/lib/gexp/receiver/mongoid.rb +3 -3
- data/lib/gexp/state_definition/state_machine.rb +8 -6
- data/lib/gexp/user.rb +9 -0
- data/lib/gexp/version.rb +1 -1
- data/spec/gexp/command/object_spec.rb +73 -26
- data/spec/gexp/handler/transition/builder_spec.rb +42 -48
- data/spec/gexp/item_spec.rb +65 -0
- data/spec/gexp/receiver_spec.rb +7 -2
- data/spec/gexp/state_definition/state_machine_spec.rb +11 -1
- data/spec/mongoid.yml +19 -0
- data/spec/spec_helper.rb +3 -0
- metadata +47 -28
@@ -2,7 +2,7 @@ module Gexp
|
|
2
2
|
class Handler
|
3
3
|
class Transition
|
4
4
|
|
5
|
-
attr_accessor :transition, :
|
5
|
+
attr_accessor :transition, :object, :subject, :provider
|
6
6
|
|
7
7
|
def self.namespaces
|
8
8
|
@namespaces ||= {
|
@@ -11,29 +11,57 @@ module Gexp
|
|
11
11
|
}
|
12
12
|
end
|
13
13
|
|
14
|
-
# transition - объект транзакции от стейт машины
|
15
|
-
# object - объект у которого вызывается событие стейтмашины
|
16
|
-
# subject - объект кто вызывает событие (всегда User)
|
17
|
-
# provider - объект которому принадлежит object (иногда User иногда Friend)
|
14
|
+
# @params [StateMachine::Transaction] transition - объект транзакции от стейт машины
|
15
|
+
# @params [Item] object - объект у которого вызывается событие стейтмашины
|
16
|
+
# @params [User] subject - объект кто вызывает событие (всегда User)
|
17
|
+
# @params [User] provider - объект которому принадлежит object (иногда User иногда Friend)
|
18
|
+
#
|
19
|
+
# @note Выбор конечного объекта определяется конфигом object
|
18
20
|
#
|
19
|
-
# Выбор конечного объекта определяется конфигом object
|
20
21
|
def initialize(transition, object = nil, subject = nil, provider = nil)
|
21
22
|
@transition = transition
|
23
|
+
actor = @transition.args.first
|
24
|
+
|
25
|
+
unless self.actor_support?(actor)
|
26
|
+
raise "Unsupported actor type #{@actor.class.name}"
|
27
|
+
else
|
28
|
+
@actor = actor
|
29
|
+
end
|
30
|
+
end
|
22
31
|
|
23
|
-
|
24
|
-
@
|
25
|
-
|
32
|
+
def object
|
33
|
+
@object ||= @actor.object
|
34
|
+
end
|
35
|
+
|
36
|
+
def subject
|
37
|
+
@subject ||= @actor.subject
|
38
|
+
end
|
26
39
|
|
27
|
-
|
40
|
+
def provider
|
41
|
+
@provider ||= @actor.provider
|
28
42
|
end
|
29
43
|
|
44
|
+
def config
|
45
|
+
@config ||= self.object.config
|
46
|
+
end
|
47
|
+
|
48
|
+
def actor_support?(actor)
|
49
|
+
!actor.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Создает объект обработчика
|
53
|
+
#
|
54
|
+
# @params [Gexp::Handler]
|
30
55
|
def produce(params, type)
|
31
56
|
producer = Gexp::Handler::Producer.new params, type
|
32
57
|
producer.emit
|
33
58
|
end
|
34
59
|
|
60
|
+
# Возвращает массив обработчиков
|
61
|
+
#
|
62
|
+
# @return [Array(Gexp::Handler)]
|
35
63
|
def handlers(type = nil)
|
36
|
-
type
|
64
|
+
type ||= :checkers
|
37
65
|
(self.send(type) || []).map do |handler_params|
|
38
66
|
self.produce(handler_params, type)
|
39
67
|
end
|
@@ -6,24 +6,50 @@ module Gexp
|
|
6
6
|
# в зависимости от перехода FSM (StateMachine)
|
7
7
|
class Builder < self
|
8
8
|
|
9
|
+
# XXX: метод не используется
|
10
|
+
# тк определение событий хранит
|
11
|
+
# только информацию по переходам
|
9
12
|
def events(last_key)
|
10
|
-
event =
|
13
|
+
event = self.config.to_hash[:states][:events][@transition.event.to_sym]
|
11
14
|
(event || {})[last_key] || []
|
12
15
|
end
|
13
16
|
|
17
|
+
# Возвращает конфиги для создания обработчиков
|
18
|
+
#
|
19
|
+
# @params [Symbol] last_key - [ :check | :modify ]
|
20
|
+
# @returns [Array]
|
21
|
+
def conf_handlers(last_key)
|
22
|
+
from = self.transition.from_name
|
23
|
+
to = self.transition.to_name
|
24
|
+
from_branch = self.config.to_hash[:states][:states][from] || {}
|
25
|
+
|
26
|
+
configs = (from_branch[to] || {})[last_key] || []
|
27
|
+
end
|
28
|
+
|
14
29
|
def transitions(last_key)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
30
|
+
conf = conf_handlers(last_key)
|
31
|
+
type = last_key == :check ? :checkers : :modifiers
|
32
|
+
|
33
|
+
observers = conf.map do |params|
|
34
|
+
|
35
|
+
producer = Gexp::Handler::Producer.new(params, type, {
|
36
|
+
object: self.object,
|
37
|
+
subject: self.subject,
|
38
|
+
provider: self.provider,
|
39
|
+
})
|
40
|
+
|
41
|
+
producer.emit
|
42
|
+
end
|
43
|
+
|
44
|
+
observers
|
19
45
|
end
|
20
46
|
|
21
47
|
def checkers
|
22
|
-
|
48
|
+
transitions(:check)
|
23
49
|
end
|
24
50
|
|
25
51
|
def modifiers
|
26
|
-
|
52
|
+
transitions(:modify)
|
27
53
|
end
|
28
54
|
|
29
55
|
end
|
data/lib/gexp/item.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module Gexp
|
2
|
+
|
3
|
+
module Item
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.instance_eval do
|
7
|
+
class << self
|
8
|
+
def inherited(sub)
|
9
|
+
sub.instance_eval do
|
10
|
+
include Gexp::Object
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def register
|
15
|
+
@register ||= {}
|
16
|
+
|
17
|
+
if @register.empty?
|
18
|
+
register = Configuration.for 'register'
|
19
|
+
register.keys.each do |key|
|
20
|
+
@register[key] = Configuration.for(key).to_hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@register
|
25
|
+
end
|
26
|
+
|
27
|
+
def reload_register!
|
28
|
+
@register = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def around_handlers(fsm_transition, &block)
|
36
|
+
# объект команды (как параметр к событию)
|
37
|
+
transition = Gexp::Handler::Transition::Builder.new(fsm_transition)
|
38
|
+
|
39
|
+
self.check_handlers(transition)
|
40
|
+
self.before_event(transition)
|
41
|
+
block.call # выполнение самого перехода
|
42
|
+
self.after_event(transition)
|
43
|
+
self.modify_handlers(transition)
|
44
|
+
rescue => e
|
45
|
+
raise e unless self.on_error(e)
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_error(exception)
|
49
|
+
# TODO: Сделать обработку ошибок перехода
|
50
|
+
end
|
51
|
+
|
52
|
+
def before_event(transition)
|
53
|
+
# TODO: сделать обработку события
|
54
|
+
end
|
55
|
+
|
56
|
+
def after_event(transition)
|
57
|
+
# TODO: сделать обработку события
|
58
|
+
end
|
59
|
+
|
60
|
+
def check_handlers(transition)
|
61
|
+
transition.checkers.each { |handler|
|
62
|
+
handler.process(*handler.params)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def modify_handlers(transition)
|
67
|
+
transition.modifiers.each { |handler|
|
68
|
+
handler.process(*handler.params)
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
data/lib/gexp/object.rb
CHANGED
@@ -7,6 +7,14 @@ module Gexp
|
|
7
7
|
include ::Gexp::StateDefinition::StateMachine
|
8
8
|
|
9
9
|
def self.included(base)
|
10
|
+
self.linked_config base
|
11
|
+
end
|
12
|
+
|
13
|
+
# Определяет конфиг как переменную класса
|
14
|
+
# Определяя ключ конфига как имя класса
|
15
|
+
#
|
16
|
+
# @params [Class]
|
17
|
+
def self.linked_config(base)
|
10
18
|
@config_name = base.name.gsub('::', '.').underscore
|
11
19
|
@end_class_name = base.name.split('::').last
|
12
20
|
|
@@ -3,13 +3,13 @@ module Gexp
|
|
3
3
|
class Mongoid < self
|
4
4
|
|
5
5
|
def receive
|
6
|
-
|
7
|
-
|
6
|
+
Gexp::Mongoid::Transaction.with do |context|
|
7
|
+
@stack.map do |command|
|
8
8
|
command.perform
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -27,12 +27,14 @@ module Gexp
|
|
27
27
|
smc = self
|
28
28
|
|
29
29
|
# State definition
|
30
|
-
sm[:states].each do |name,
|
31
|
-
smc.state name
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
sm[:states].each do |name, hash|
|
31
|
+
smc.state name.to_sym
|
32
|
+
# method = hash
|
33
|
+
# smc.state name do
|
34
|
+
# (methods || []).each do |method|
|
35
|
+
# self.send(method)
|
36
|
+
# end
|
37
|
+
# end
|
36
38
|
end
|
37
39
|
|
38
40
|
# Events definition
|
data/lib/gexp/user.rb
ADDED
data/lib/gexp/version.rb
CHANGED
@@ -5,9 +5,8 @@ describe Gexp::Command::Object do
|
|
5
5
|
|
6
6
|
context "Команда pick на объекте у себя в локации" do
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
@request = HashWithIndifferentAccess.new({
|
8
|
+
let(:request) do
|
9
|
+
HashWithIndifferentAccess.new({
|
11
10
|
:params => {
|
12
11
|
:sended_at => 123456789.012,
|
13
12
|
:create_at => 123456789.012,
|
@@ -23,57 +22,105 @@ describe Gexp::Command::Object do
|
|
23
22
|
}]
|
24
23
|
}
|
25
24
|
})
|
25
|
+
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
let(:context) { Object.new }
|
28
|
+
let(:object) { ItemExample.new }
|
29
|
+
let(:user) do
|
30
|
+
user = UserExample.new
|
31
|
+
user.energy = 1
|
32
|
+
user.wood = 5
|
33
|
+
user
|
34
|
+
end
|
35
|
+
let(:command) do
|
36
|
+
command = Gexp::Command::Object.new request[:params][:commands].first
|
37
|
+
command.context = context
|
38
|
+
stub(command).subject { user }
|
39
|
+
command
|
40
|
+
end
|
36
41
|
|
42
|
+
before do
|
43
|
+
stub(ItemExample).find.with('55a55') { object }
|
37
44
|
end
|
38
45
|
|
39
46
|
it "Команда должна загружать объекты" do
|
40
|
-
|
47
|
+
command.object.should == object
|
41
48
|
end
|
42
49
|
|
43
50
|
it "Для конмады определенно событие" do
|
44
|
-
|
51
|
+
command.event.should == :pick
|
45
52
|
end
|
46
53
|
|
47
54
|
it "Нельзя (так просто взять и) создать команду без параметра-объекта" do
|
48
|
-
params =
|
55
|
+
params = request[:params][:commands].first
|
49
56
|
params.delete(:objects)
|
50
57
|
lambda {
|
51
|
-
|
58
|
+
command = Gexp::Command::Object.new params
|
52
59
|
}.should raise_error Regexp.new("Can't find object")
|
53
60
|
end
|
54
61
|
|
55
62
|
context "#perform" do
|
56
63
|
|
57
|
-
it "
|
58
|
-
mock(
|
59
|
-
|
60
|
-
|
64
|
+
it "должен вызывать соотвествующее соьытие у объекта" do
|
65
|
+
mock(object).pick.with_any_args.once { true }
|
66
|
+
command.perform
|
67
|
+
command.should be_done
|
68
|
+
end
|
69
|
+
|
70
|
+
context "при успешном выполнении команды" do
|
71
|
+
|
72
|
+
it "до вызова объект в первоначальном состоянии" do
|
73
|
+
object.should be_created
|
74
|
+
end
|
75
|
+
|
76
|
+
it "ошибок быть не должно" do
|
77
|
+
command.perform
|
78
|
+
command.errors.should be_empty
|
79
|
+
end
|
80
|
+
|
81
|
+
it "состояние у объекта меняется" do
|
82
|
+
command.perform
|
83
|
+
object.should be_prebuilded
|
84
|
+
end
|
85
|
+
|
86
|
+
it "у объекта вызываются калбеки" do
|
87
|
+
mock(object).modify_handlers(anything)
|
88
|
+
mock(object).check_handlers(anything)
|
89
|
+
mock(object).before_event(anything)
|
90
|
+
mock(object).after_event(anything)
|
91
|
+
|
92
|
+
command.perform
|
93
|
+
end
|
94
|
+
|
95
|
+
it "вызываются конструкторы обработчиков"
|
96
|
+
|
97
|
+
it "у обработчиков действия вызывается #process"
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
context "при ошибке обработчика" do
|
102
|
+
|
103
|
+
it "последующие обработчики не вызываются"
|
104
|
+
|
105
|
+
it "состояние объекта не изменяется"
|
106
|
+
|
61
107
|
end
|
62
108
|
|
63
109
|
context "При неуспешном выполнении" do
|
64
110
|
|
65
111
|
before do
|
66
|
-
mock(
|
112
|
+
mock(object).pick.with_any_args.once { raise 'Something wrong' }
|
67
113
|
end
|
68
114
|
|
69
115
|
it "Команда должна находится в статусе failed" do
|
70
|
-
|
71
|
-
|
116
|
+
lambda { command.perform }.should raise_error
|
117
|
+
command.should be_failed
|
72
118
|
end
|
73
119
|
|
74
|
-
it "исключение должно агрегироваться в
|
75
|
-
|
76
|
-
|
120
|
+
it "исключение должно агрегироваться в #errors" do
|
121
|
+
lambda { command.perform }.should raise_error
|
122
|
+
command.errors.should_not be_empty
|
123
|
+
command.errors.last.message.should == 'Something wrong'
|
77
124
|
end
|
78
125
|
|
79
126
|
end
|
@@ -5,33 +5,30 @@ describe Gexp::Handler::Transition::Builder do
|
|
5
5
|
|
6
6
|
shared_examples_for Gexp::Handler::Transition::Builder do
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
let(:object) do
|
9
|
+
object = ItemExample.new
|
10
|
+
mock(object).config { config }
|
11
|
+
object
|
12
|
+
end
|
11
13
|
|
12
|
-
|
14
|
+
let(:actor) {
|
15
|
+
actor = Object.new
|
16
|
+
stub(actor).object { object }
|
17
|
+
stub(actor).subject {}
|
18
|
+
stub(actor).provider {}
|
19
|
+
actor
|
20
|
+
}
|
13
21
|
|
14
22
|
let(:transition) do
|
15
23
|
transition = Object.new
|
16
24
|
stub(transition).to_name { to }
|
17
25
|
stub(transition).from_name { from }
|
18
26
|
stub(transition).event { event }
|
27
|
+
stub(transition).args { [ actor ] }
|
19
28
|
|
20
29
|
transition
|
21
30
|
end
|
22
31
|
|
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
32
|
let(:object_param) do
|
36
33
|
object = Object.new
|
37
34
|
stub(object).config { config }
|
@@ -46,12 +43,12 @@ describe Gexp::Handler::Transition::Builder do
|
|
46
43
|
provider_param
|
47
44
|
end
|
48
45
|
|
49
|
-
it "
|
50
|
-
subject.
|
46
|
+
it "должен возвращать массив чекеров" do
|
47
|
+
subject.conf_handlers(:check).should == result_chekers
|
51
48
|
end
|
52
49
|
|
53
|
-
it "
|
54
|
-
subject.
|
50
|
+
it "должен возвращать массив модифаеров" do
|
51
|
+
subject.conf_handlers(:modify).should == result_modifiers
|
55
52
|
end
|
56
53
|
|
57
54
|
end
|
@@ -63,54 +60,51 @@ describe Gexp::Handler::Transition::Builder do
|
|
63
60
|
let(:from) { :created }
|
64
61
|
let(:event) { :place }
|
65
62
|
let(:config) do
|
66
|
-
|
63
|
+
conf = Object.new
|
64
|
+
stub(conf).to_hash { { states: {
|
67
65
|
events: {
|
68
|
-
place:
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
}
|
66
|
+
place: [
|
67
|
+
{ from: :created, to: :prebuilded },
|
68
|
+
],
|
69
|
+
pick: [
|
70
|
+
{ from: :prebuilded, to: :postbuilded },
|
71
|
+
{ from: :postbuilded, to: :builded },
|
72
|
+
{ from: :builded, to: :builded },
|
73
|
+
],
|
74
|
+
sell: [
|
75
|
+
{ from: :prebuilded, to: :selled },
|
76
|
+
{ from: :postbuilded, to: :selled },
|
77
|
+
{ from: :builded, to: :selled },
|
78
|
+
]
|
82
79
|
},
|
83
|
-
|
80
|
+
states: { # TODO!: Переименовать в transitions
|
84
81
|
created: {
|
85
82
|
selled: { },
|
86
83
|
prebuilded: {
|
87
84
|
check: [
|
88
|
-
[ :shared_resources, :
|
85
|
+
[ :shared_resources, :subject, { 0 => 5 } ]
|
89
86
|
],
|
90
87
|
modify: [
|
91
|
-
[ :shared_resources, :
|
92
|
-
[ :resources, :
|
93
|
-
[ :shared_resources, :
|
88
|
+
[ :shared_resources, :subject, { 0 => 5 } ],
|
89
|
+
[ :resources, :subject, { wood: -5, energy: -1 } ],
|
90
|
+
[ :shared_resources, :subject, { 0 => -5 } ]
|
94
91
|
]
|
95
92
|
}
|
96
93
|
}
|
97
94
|
}
|
98
|
-
}
|
95
|
+
} } }
|
99
96
|
end
|
100
97
|
|
101
98
|
let(:result_chekers) {
|
102
99
|
[
|
103
|
-
[ :
|
104
|
-
[ :object, [ :place_allowed?, :not_blocked? ] ],
|
105
|
-
[ :shared_resources, :object, { 0 => 5 } ],
|
100
|
+
[ :shared_resources, :subject, { 0 => 5 } ],
|
106
101
|
]
|
107
102
|
}
|
108
103
|
let(:result_modifiers) {
|
109
104
|
[
|
110
|
-
[ :
|
111
|
-
[ :
|
112
|
-
[ :
|
113
|
-
[ :shared_resources, :object, { 0 => -5 } ]
|
105
|
+
[ :shared_resources, :subject, { 0 => 5 } ],
|
106
|
+
[ :resources, :subject, { wood: -5, energy: -1 } ],
|
107
|
+
[ :shared_resources, :subject, { 0 => -5 } ]
|
114
108
|
]
|
115
109
|
}
|
116
110
|
let(:subject_param) { Object.new }
|