gexp 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 }