gexp 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 - событие
@@ -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
+ * Как извлекаются обработчики?
@@ -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
- class ItemExample
2
- class << self
3
- def inherited(sub)
4
- sub.instance_eval do
5
- include Gexp::Object
6
- end
7
- end
8
-
9
- def register
10
- @register ||= {}
11
-
12
- if @register.empty?
13
- register = Configuration.for 'register'
14
- register.keys.each do |key|
15
- @register[key] = Configuration.for(key).to_hash
16
- end
17
- end
18
-
19
- @register
20
- end
21
-
22
- def reload_register!
23
- @register = {}
24
- end
25
-
26
- end
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
- attr_accessor :current_command
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
@@ -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
- raise 'Not defined perform method'
64
+
65
+ raise NotImplementedError.new \
66
+ 'Not defined perform method'
51
67
  end
52
68
 
53
69
  end
@@ -1,9 +1,14 @@
1
1
  module Gexp
2
2
  class Command
3
- class Object < self
4
3
 
5
- attr_accessor :event # TODO: only getter
6
- attr_accessor :object
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
@@ -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 = {} # for ruby 1.9.x
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?
@@ -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
- # object - объект над котым производится обработка
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 || {})[:self]
15
+ @user = (@objects || {})[:subject]
17
16
  @full_params = full_params
18
17
  end
19
18
 
20
19
  def process(params = nil)
21
- raise 'Override process handler method'
20
+ raise NotImplementedError.new \
21
+ 'Override process handler method'
22
22
  end
23
23
 
24
24
  end
@@ -49,6 +49,7 @@ module Gexp
49
49
  def process(params = {}, &block)
50
50
  @changes = {}
51
51
  @params = params if @params.empty?
52
+ @params = @params.first if @params.is_a?(Array)
52
53
  @params.each do |resource, value|
53
54
  if @user.respond_to?(resource.to_sym)
54
55
 
@@ -7,18 +7,21 @@ module Gexp
7
7
  # из конфигурационного файл объекта
8
8
  class Producer
9
9
 
10
- def self.namespaces
11
- {
12
- chekers: Gexp::Handler::Check,
13
- modifiers: Gexp::Handler::Modify,
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 - handler params { klass, object, args }
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.classify
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
- def for_klass?
52
- @params.size == 3
53
- end
59
+ # Параметры под класс
60
+ #
61
+ # @return [Boolean]
62
+ def for_klass?
63
+ @params.size == 3
64
+ end
54
65
 
55
- def for_caller?
56
- @params.size == 2
57
- end
66
+ # Параметры под коллер
67
+ #
68
+ # @return [Boolean]
69
+ def for_caller?
70
+ @params.size == 2
71
+ end
58
72
 
59
73
  end
60
74
  end