gexp 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,72 @@
|
|
1
|
+
# Обработка команд
|
2
|
+
|
3
|
+
С примером обработки команд можно ознакомиться
|
4
|
+
в спецификации
|
5
|
+
|
6
|
+
```
|
7
|
+
spec/gexp/item_spec.rb
|
8
|
+
```
|
9
|
+
|
10
|
+
Данный документ содержит описание взаимодействия объектов при получении запроса от сервера.
|
11
|
+
|
12
|
+
### Описание внешнего API
|
13
|
+
|
14
|
+
Основным классом по внешниму взаимодействию
|
15
|
+
является абстрактный класс Gexp::Receiver.
|
16
|
+
|
17
|
+
Его конечная реализация должна обеспечивать транзакционность выполнения пачки (batch) комманд (пример находится в lib/gexp/receiver/example.rb).
|
18
|
+
|
19
|
+
Интерфейс состоит из нескольких операций
|
20
|
+
|
21
|
+
1) Создание объекта Receiver
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
Gexp::Receiver::Example.new user, request
|
25
|
+
```
|
26
|
+
|
27
|
+
В конструктор объекта передается объект пользователя (над которым может осуществляться транзакция) а также объект запроса вида
|
28
|
+
|
29
|
+
{
|
30
|
+
params: {
|
31
|
+
sended_at: <>,
|
32
|
+
created_at: <>,
|
33
|
+
commands: [...]
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
В параметре commands находится содержимое пачки команд (batch), в виде хешей вида
|
38
|
+
|
39
|
+
{
|
40
|
+
event: :<event_name>,
|
41
|
+
stage: { x: <0>, y: <0> },
|
42
|
+
rewards: { <resource_name>: <value> },
|
43
|
+
timestamp: <0.0>,
|
44
|
+
objects: { '<class_name>': '<hash_id>' },
|
45
|
+
transition: { <from_state>: <to_state> },
|
46
|
+
seed: <random_seed>
|
47
|
+
}
|
48
|
+
|
49
|
+
```
|
50
|
+
TODO: Сделать objects и transition массивами хешей, чтобы задавать переходы для каждого объекта
|
51
|
+
```
|
52
|
+
|
53
|
+
2) Создание комманд
|
54
|
+
|
55
|
+
При создании объекта Receiver создается объект
|
56
|
+
Gexp::Command::Stack, который выражает пачку команд.
|
57
|
+
|
58
|
+
При создании этого объекта создаются объекты под каждую команду.
|
59
|
+
|
60
|
+
Команда имет 4 состояния
|
61
|
+
|
62
|
+
1. new - создана
|
63
|
+
2. active - начала исполнение
|
64
|
+
3. done - выполнена успешно
|
65
|
+
4. failed - проваленна
|
66
|
+
|
67
|
+
У команды есть поля
|
68
|
+
|
69
|
+
* errors - массив ошибок
|
70
|
+
* context - хеш контекста
|
71
|
+
* params - первоначальные параметры
|
72
|
+
* event - событие
|
data/docs/notes.mdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# FAQ
|
2
|
+
|
3
|
+
* Как передается окружение в обработчик?
|
4
|
+
|
5
|
+
- Через команду. Объект команды передается вместе с событием
|
6
|
+
и передается в калбеки обработки в качетве актора.
|
7
|
+
|
8
|
+
В калбеках из него извлекаются обработчики и исполняются
|
9
|
+
|
10
|
+
* Как выполняются действия над пользователем?
|
11
|
+
- Он фигурирует в команде в качетве объекта, те
|
12
|
+
события над пользователем также вызываются
|
13
|
+
через FSM. Так проще контролировать доступ
|
14
|
+
к функционалу
|
15
|
+
|
16
|
+
* Как передаются другие контексты?
|
17
|
+
- У команды есть объект context
|
18
|
+
* Как передаются контекст друга?
|
19
|
+
* Как извлекаются обработчики?
|
data/gexp.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |gem|
|
|
24
24
|
gem.add_development_dependency "rr"
|
25
25
|
gem.add_development_dependency "rake"
|
26
26
|
gem.add_development_dependency "pry"
|
27
|
+
gem.add_development_dependency "configuration"
|
27
28
|
gem.add_development_dependency "state_machine"
|
28
29
|
gem.add_development_dependency "activesupport"
|
29
30
|
gem.add_development_dependency 'bson', '= 1.8.0'
|
@@ -1,33 +1,38 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
1
|
+
require 'configuration'
|
2
|
+
|
3
|
+
Configuration.for(:item_example) do
|
4
|
+
states {
|
5
|
+
initial :created
|
6
|
+
states { # TODO!: Переименовать в transitions
|
7
|
+
# для этого нужны изменения в Gexp::Handler::Transition::Builder
|
8
|
+
created {
|
9
|
+
prebuilded {
|
10
|
+
modify [
|
11
|
+
[ :resources, :subject, { wood: -5, energy: -1 } ],
|
12
|
+
]
|
13
|
+
}
|
14
|
+
}
|
15
|
+
prebuilded {}
|
16
|
+
postbuild {}
|
17
|
+
builded {}
|
18
|
+
}
|
19
|
+
|
20
|
+
events {
|
21
|
+
pick [
|
22
|
+
{ from: :created, to: :prebuilded },
|
23
|
+
{ from: :prebuilded, to: :postbuilded },
|
24
|
+
{ from: :postbuilded, to: :builded },
|
25
|
+
{ from: :builded, to: :builded },
|
26
|
+
]
|
27
|
+
}
|
28
|
+
}
|
29
|
+
end
|
27
30
|
|
28
|
-
|
31
|
+
class ItemExample
|
29
32
|
|
30
33
|
include Mongoid::Document
|
34
|
+
include Gexp::Item
|
35
|
+
include Gexp::Object
|
31
36
|
|
32
37
|
field :state, type: String, default: 'created'
|
33
38
|
field :uid, type: String
|
@@ -40,29 +45,4 @@ class ItemExample
|
|
40
45
|
where(_type: klass, uid: uid, social_type_id: social_type_id)
|
41
46
|
}
|
42
47
|
|
43
|
-
# TODO: по возможности переделать на Mongoid::Observer или скрыть из конфига
|
44
|
-
def around_handlers(transition, &block)
|
45
|
-
self.check_handlers(transition)
|
46
|
-
self.before_event(transition)
|
47
|
-
#require 'pry'
|
48
|
-
#binding.pry
|
49
|
-
block.call
|
50
|
-
self.after_event(transition)
|
51
|
-
self.modify_handlers(transition)
|
52
|
-
rescue => e
|
53
|
-
raise
|
54
|
-
end
|
55
|
-
|
56
|
-
def before_event(transition)
|
57
|
-
end
|
58
|
-
|
59
|
-
def after_event(transition)
|
60
|
-
end
|
61
|
-
|
62
|
-
def check_handlers(transition)
|
63
|
-
end
|
64
|
-
|
65
|
-
def modify_handlers(transition)
|
66
|
-
end
|
67
|
-
|
68
48
|
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
class UserExample
|
2
|
+
|
2
3
|
include Mongoid::Document
|
4
|
+
include Gexp::User
|
3
5
|
|
4
|
-
field :energy, type: Integer
|
5
|
-
field :exp, type: Integer
|
6
|
-
field :exp_level, type: Integer
|
6
|
+
field :energy, type: Integer, default: 0
|
7
|
+
field :exp, type: Integer, default: 0
|
8
|
+
field :exp_level, type: Integer, default: 0
|
9
|
+
field :wood, type: Integer, default: 0
|
7
10
|
|
8
11
|
def max_energy
|
9
12
|
[ 10, 40, 50, 60, 80, 90, 100][self.exp_level || 0]
|
@@ -11,4 +14,5 @@ class UserExample
|
|
11
14
|
|
12
15
|
def after_change!(param)
|
13
16
|
end
|
17
|
+
|
14
18
|
end
|
data/lib/gexp/command.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
require 'state_machine/core'
|
3
3
|
|
4
4
|
module Gexp
|
5
|
+
|
6
|
+
# ,Базовый класс комманд
|
5
7
|
class Command
|
6
8
|
|
7
9
|
extend StateMachine::MacroMethods
|
@@ -10,6 +12,12 @@ module Gexp
|
|
10
12
|
attr_accessor :context
|
11
13
|
attr_accessor :params
|
12
14
|
|
15
|
+
attr_accessor :event # TODO: only getter
|
16
|
+
|
17
|
+
attr_accessor :object
|
18
|
+
attr_accessor :subject
|
19
|
+
attr_accessor :provider
|
20
|
+
|
13
21
|
state_machine :initial => :new do
|
14
22
|
state :active
|
15
23
|
state :done
|
@@ -35,6 +43,9 @@ module Gexp
|
|
35
43
|
super() # Инициализация StateMachine
|
36
44
|
end
|
37
45
|
|
46
|
+
# индетификатор комманды
|
47
|
+
#
|
48
|
+
# @return [String]
|
38
49
|
def hash
|
39
50
|
# TODO: Заменить на BSON:ObjectId
|
40
51
|
[
|
@@ -44,10 +55,15 @@ module Gexp
|
|
44
55
|
].join('_')
|
45
56
|
end
|
46
57
|
|
58
|
+
# Заглушка для исполнения команды
|
59
|
+
#
|
60
|
+
# @raise NotImplementedError
|
47
61
|
def perform
|
48
62
|
self.activate!
|
49
63
|
self.failure!
|
50
|
-
|
64
|
+
|
65
|
+
raise NotImplementedError.new \
|
66
|
+
'Not defined perform method'
|
51
67
|
end
|
52
68
|
|
53
69
|
end
|
data/lib/gexp/command/object.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
module Gexp
|
2
2
|
class Command
|
3
|
-
class Object < self
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
# Объектная команда
|
5
|
+
#
|
6
|
+
# Команда выполняющаяся над объектами
|
7
|
+
# игрового мира.
|
8
|
+
#
|
9
|
+
# Выполняет событие FSM у объекта описанное
|
10
|
+
# в параметрах.
|
11
|
+
class Object < self
|
7
12
|
|
8
13
|
def initialize(params = {})
|
9
14
|
super
|
@@ -11,15 +16,25 @@ module Gexp
|
|
11
16
|
self.load_object
|
12
17
|
end
|
13
18
|
|
19
|
+
# Выполнение команды
|
14
20
|
def perform
|
15
21
|
self.activate!
|
16
|
-
@object.send(self.event)
|
22
|
+
@object.send(self.event, self)
|
17
23
|
self.complete!
|
18
24
|
rescue => e
|
19
|
-
self.errors << e
|
20
25
|
self.failure!
|
26
|
+
self.errors << e
|
27
|
+
raise e
|
21
28
|
end
|
22
29
|
|
30
|
+
def subject
|
31
|
+
self.context.user
|
32
|
+
end
|
33
|
+
|
34
|
+
#def provider
|
35
|
+
# self.context.friend
|
36
|
+
#end
|
37
|
+
|
23
38
|
protected
|
24
39
|
|
25
40
|
def load_object
|
data/lib/gexp/command/stack.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
module Gexp
|
2
2
|
class Command
|
3
|
+
|
4
|
+
# Стек комманд.
|
5
|
+
#
|
6
|
+
# Принимает реквест и разбивает его
|
7
|
+
# батч на команды
|
3
8
|
class Stack
|
4
9
|
|
5
10
|
include Enumerable
|
@@ -9,14 +14,20 @@ module Gexp
|
|
9
14
|
def initialize(user, request)
|
10
15
|
@user = user
|
11
16
|
@request = request
|
12
|
-
@commands = {} #
|
17
|
+
@commands = {} # для ruby 1.9.x иначе OrderedHash
|
13
18
|
self.fill()
|
14
19
|
end
|
15
20
|
|
21
|
+
# Фабрика команд по их параметрам
|
16
22
|
def build_command(command_param = {})
|
23
|
+
# XXX: пока один поддерживаемый тип комманд
|
24
|
+
# TODO: Расширять типы команд отсюда,
|
25
|
+
# возможно нужен конфиг соотвествия
|
26
|
+
# Тип События - Тип команды
|
17
27
|
Gexp::Command::Object.new(command_param)
|
18
28
|
end
|
19
29
|
|
30
|
+
# Заполняет хеш команд по реквесту
|
20
31
|
def fill(request = nil)
|
21
32
|
request ||= @request
|
22
33
|
@commands = {}
|
@@ -31,12 +42,15 @@ module Gexp
|
|
31
42
|
@commands.size
|
32
43
|
end
|
33
44
|
|
45
|
+
# Просто перебор команд для Enumerable
|
34
46
|
def each(&block)
|
35
47
|
@commands.each(&block)
|
36
48
|
end
|
37
49
|
|
38
50
|
protected
|
39
51
|
|
52
|
+
# Добавляет команду в общий хеш
|
53
|
+
# и задает контекст
|
40
54
|
def add(command)
|
41
55
|
command.context = self
|
42
56
|
if @commands[command.hash].present?
|
data/lib/gexp/handler.rb
CHANGED
@@ -2,23 +2,23 @@ module Gexp
|
|
2
2
|
class Handler
|
3
3
|
# Базовый класс обработчиков
|
4
4
|
|
5
|
-
attr_accessor :user, :object
|
5
|
+
attr_accessor :user, :object, :params, :objects
|
6
6
|
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
7
|
+
# @params [Object] object - объект над котым производится обработка
|
8
|
+
# @params [Hash] params
|
9
|
+
# @params [Array] objects
|
10
|
+
# @params []
|
12
11
|
def initialize(object = nil, params = {}, objects = nil, full_params = nil)
|
13
12
|
@object = object
|
14
13
|
@params = params
|
15
14
|
@objects = objects
|
16
|
-
@user = (@objects || {})[:
|
15
|
+
@user = (@objects || {})[:subject]
|
17
16
|
@full_params = full_params
|
18
17
|
end
|
19
18
|
|
20
19
|
def process(params = nil)
|
21
|
-
raise
|
20
|
+
raise NotImplementedError.new \
|
21
|
+
'Override process handler method'
|
22
22
|
end
|
23
23
|
|
24
24
|
end
|
@@ -7,18 +7,21 @@ module Gexp
|
|
7
7
|
# из конфигурационного файл объекта
|
8
8
|
class Producer
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# @return [Hash] - хеш классов обработчиков
|
13
|
+
def namespaces
|
14
|
+
{
|
15
|
+
chekers: Gexp::Handler::Check,
|
16
|
+
modifiers: Gexp::Handler::Modify,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
15
20
|
end
|
16
21
|
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# params
|
20
|
-
# type - chekers|modifiers # TODO: избавиться от этого параметра
|
21
|
-
# objects - { object: <...>, subject: <...>, provider: <...> }
|
22
|
+
# @params [Hash] params - handler params { klass, object, args }
|
23
|
+
# @params [String] type - chekers|modifiers # TODO: избавиться от этого параметра
|
24
|
+
# @params [Hash] objects - { object: <...>, subject: <...>, provider: <...> }
|
22
25
|
def initialize(params, type = nil, objects = {})
|
23
26
|
@params = params
|
24
27
|
@type = type
|
@@ -29,12 +32,17 @@ module Gexp
|
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
35
|
+
# Создает и возвращает класс обработчика
|
36
|
+
#
|
37
|
+
# @return [Gexp::Handler]
|
32
38
|
def emit
|
33
39
|
args = @params.clone
|
34
40
|
|
35
41
|
if for_klass?
|
36
42
|
superclass = self.class.namespaces[@type]
|
37
|
-
subclass = args.shift.to_s.
|
43
|
+
subclass = args.shift.to_s.humanize
|
44
|
+
|
45
|
+
# TODO: Сделать проверку существования класса
|
38
46
|
klass = superclass.const_get(subclass)
|
39
47
|
else
|
40
48
|
klass = Gexp::Handler::Caller
|
@@ -48,13 +56,19 @@ module Gexp
|
|
48
56
|
|
49
57
|
protected
|
50
58
|
|
51
|
-
|
52
|
-
|
53
|
-
|
59
|
+
# Параметры под класс
|
60
|
+
#
|
61
|
+
# @return [Boolean]
|
62
|
+
def for_klass?
|
63
|
+
@params.size == 3
|
64
|
+
end
|
54
65
|
|
55
|
-
|
56
|
-
|
57
|
-
|
66
|
+
# Параметры под коллер
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
def for_caller?
|
70
|
+
@params.size == 2
|
71
|
+
end
|
58
72
|
|
59
73
|
end
|
60
74
|
end
|