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.
@@ -2,7 +2,7 @@ module Gexp
2
2
  class Handler
3
3
  class Transition
4
4
 
5
- attr_accessor :transition, :config, :object, :subject, :provider
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
- @object = object
24
- @subject = subject
25
- @provider = provider
32
+ def object
33
+ @object ||= @actor.object
34
+ end
35
+
36
+ def subject
37
+ @subject ||= @actor.subject
38
+ end
26
39
 
27
- @config = @object.config
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 = :checkers
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 = @config[:events][@transition.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
- from = @transition.from_name
16
- to = @transition.to_name
17
- from_branch = @config[:transitions][from] || {}
18
- (from_branch[to] || {})[last_key] || []
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
- events(:check) + transitions(:check)
48
+ transitions(:check)
23
49
  end
24
50
 
25
51
  def modifiers
26
- events(:modify) + transitions(:modify)
52
+ transitions(:modify)
27
53
  end
28
54
 
29
55
  end
@@ -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
@@ -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,11 +3,11 @@ module Gexp
3
3
  class Example < self
4
4
 
5
5
  def receive
6
+ # Start transaction
6
7
  @stack.map do |hash, command|
7
- # Start transaction
8
8
  command.perform
9
- # finish transaction
10
9
  end
10
+ # finish transaction
11
11
  end
12
12
 
13
13
  end
@@ -3,13 +3,13 @@ module Gexp
3
3
  class Mongoid < self
4
4
 
5
5
  def receive
6
- @stack.map do |command|
7
- Gexp::Mongoid::Transaction.with do |context|
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, methods|
31
- smc.state name do
32
- (methods || []).each do |method|
33
- self.send(method)
34
- end
35
- end
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
@@ -0,0 +1,9 @@
1
+ module Gexp
2
+ module User
3
+
4
+ def after_change!(param)
5
+ raise NotImplementedError.new
6
+ end
7
+
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Gexp
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -5,9 +5,8 @@ describe Gexp::Command::Object do
5
5
 
6
6
  context "Команда pick на объекте у себя в локации" do
7
7
 
8
- before do
9
- @user = UserExample.new
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
- @object = Object.new
28
- @context = Object.new
29
-
30
- stub(ItemExample).find.with('55a55') { @object }
31
-
32
- lambda {
33
- @command = Gexp::Command::Object.new @request[:params][:commands].first
34
- @command.context = @context
35
- }.should_not raise_error
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
- @command.object.should == @object
47
+ command.object.should == object
41
48
  end
42
49
 
43
50
  it "Для конмады определенно событие" do
44
- @command.event.should == :pick
51
+ command.event.should == :pick
45
52
  end
46
53
 
47
54
  it "Нельзя (так просто взять и) создать команду без параметра-объекта" do
48
- params = @request[:params][:commands].first
55
+ params = request[:params][:commands].first
49
56
  params.delete(:objects)
50
57
  lambda {
51
- @command = Gexp::Command::Object.new params
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 "При успешном выполнении должен вызывать соотвествующее соьытие у объекта" do
58
- mock(@object).pick.once { true }
59
- @command.perform
60
- @command.should be_done
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(@object).pick.once { raise 'Something wrong' }
112
+ mock(object).pick.with_any_args.once { raise 'Something wrong' }
67
113
  end
68
114
 
69
115
  it "Команда должна находится в статусе failed" do
70
- @command.perform
71
- @command.should be_failed
116
+ lambda { command.perform }.should raise_error
117
+ command.should be_failed
72
118
  end
73
119
 
74
- it "исключение должно агрегироваться в соотвествующем поле" do
75
- @command.perform
76
- @command.errors.should_not be_empty
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
- # let(:to) { to }
9
-
10
- # let(:from) { from }
8
+ let(:object) do
9
+ object = ItemExample.new
10
+ mock(object).config { config }
11
+ object
12
+ end
11
13
 
12
- # let(:event) { event }
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 "should must be given result checkers" do
50
- subject.checkers.should == result_chekers
46
+ it "должен возвращать массив чекеров" do
47
+ subject.conf_handlers(:check).should == result_chekers
51
48
  end
52
49
 
53
- it "should must be given result modifiers" do
54
- subject.modifiers.should == result_modifiers
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
- check: [
70
- [ :resources, :object, { wood: 5, energy: 1 } ],
71
- [ :object, [ :place_allowed?, :not_blocked? ] ],
72
- ],
73
- modify: [
74
- [ :object, [ :change_tile_type! ] ]
75
- ]
76
- },
77
- sell: {
78
- check: [
79
- [ :object, [ :has_current_user? ] ]
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
- transitions: {
80
+ states: { # TODO!: Переименовать в transitions
84
81
  created: {
85
82
  selled: { },
86
83
  prebuilded: {
87
84
  check: [
88
- [ :shared_resources, :object, { 0 => 5 } ]
85
+ [ :shared_resources, :subject, { 0 => 5 } ]
89
86
  ],
90
87
  modify: [
91
- [ :shared_resources, :object, { 0 => 5 } ],
92
- [ :resources, :object, { wood: -5, energy: -1 } ],
93
- [ :shared_resources, :object, { 0 => -5 } ]
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
- [ :resources, :object, { wood: 5, energy: 1 } ],
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
- [ :object, [ :change_tile_type! ] ],
111
- [ :shared_resources, :object, { 0 => 5 } ],
112
- [ :resources, :object, { wood: -5, energy: -1 } ],
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 }