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