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