gexp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +29 -0
  5. data/Rakefile +1 -0
  6. data/gexp.gemspec +32 -0
  7. data/lib/gexp.rb +16 -0
  8. data/lib/gexp/command.rb +50 -0
  9. data/lib/gexp/command/object.rb +37 -0
  10. data/lib/gexp/command/stack.rb +50 -0
  11. data/lib/gexp/handler.rb +25 -0
  12. data/lib/gexp/handler/caller.rb +8 -0
  13. data/lib/gexp/handler/check.rb +9 -0
  14. data/lib/gexp/handler/check/item.rb +34 -0
  15. data/lib/gexp/handler/check/resources.rb +56 -0
  16. data/lib/gexp/handler/modify.rb +9 -0
  17. data/lib/gexp/handler/modify/resources.rb +81 -0
  18. data/lib/gexp/handler/producer.rb +61 -0
  19. data/lib/gexp/handler/transition.rb +44 -0
  20. data/lib/gexp/handler/transition/builder.rb +33 -0
  21. data/lib/gexp/mongoid.rb +4 -0
  22. data/lib/gexp/mongoid/transaction.rb +266 -0
  23. data/lib/gexp/object.rb +39 -0
  24. data/lib/gexp/receiver.rb +33 -0
  25. data/lib/gexp/state_definition.rb +4 -0
  26. data/lib/gexp/state_definition/state_machine.rb +90 -0
  27. data/lib/gexp/version.rb +3 -0
  28. data/spec/gexp/command/object_spec.rb +84 -0
  29. data/spec/gexp/command/stack_spec.rb +74 -0
  30. data/spec/gexp/command_spec.rb +59 -0
  31. data/spec/gexp/handler/check/item_spec.rb +45 -0
  32. data/spec/gexp/handler/check/resources_spec.rb +81 -0
  33. data/spec/gexp/handler/check_spec.rb +5 -0
  34. data/spec/gexp/handler/modify/resources_spec.rb +96 -0
  35. data/spec/gexp/handler/modify_spec.rb +6 -0
  36. data/spec/gexp/handler/producer_spec.rb +85 -0
  37. data/spec/gexp/handler/transition/builder_spec.rb +122 -0
  38. data/spec/gexp/handler/transition_spec.rb +14 -0
  39. data/spec/gexp/mongoid/transaction_spec.rb +210 -0
  40. data/spec/gexp/state_definition/state_machine_spec.rb +48 -0
  41. data/spec/spec_helper.rb +10 -0
  42. metadata +210 -0
@@ -0,0 +1,33 @@
1
+ module Gexp
2
+ class Receiver
3
+
4
+ class << self
5
+
6
+ def current=(receiver)
7
+ @current = receiver
8
+ end
9
+
10
+ def current
11
+ @receiver ||= self.new
12
+ end
13
+
14
+ end
15
+
16
+ def initialize(user, request)
17
+ @user = user
18
+ @request = request
19
+
20
+ @stack = Gexp::Command::Stack.new \
21
+ @user, @request
22
+ end
23
+
24
+ def receive
25
+ @stack.map do |command|
26
+ Gexp::Mongoid::Transaction.with do |context|
27
+ command.perform
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ module Gexp
2
+ module StateDefinition
3
+ end
4
+ end
@@ -0,0 +1,90 @@
1
+ module Gexp
2
+ module StateDefinition
3
+ module StateMachine
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ def define_state_by(state_configs)
13
+ sm = state_configs
14
+ state_aliases = [ :all, :any ]
15
+
16
+ self.instance_eval do
17
+ state_machine initial: sm[:initial] do
18
+
19
+ # Внешний хук от Handlers
20
+ around_transition :around_handlers
21
+
22
+ # State Machine Context
23
+ smc = self
24
+
25
+ # State definition
26
+ sm[:states].each do |name, methods|
27
+ smc.state name do
28
+ (methods || []).each do |method|
29
+ self.send(method)
30
+ end
31
+ end
32
+ end
33
+
34
+ # Events definition
35
+ sm[:events].each do |name, transitions|
36
+
37
+ smc.event name do
38
+
39
+ transitions.each do |opts|
40
+ opts[:if] ||= []
41
+ opts[:do] ||= []
42
+
43
+ from = opts[:from] || :any
44
+ to = opts[:to] || :any
45
+
46
+ from = send(from) if state_aliases.include?(from)
47
+ to = send(to) if state_aliases.include?(to)
48
+
49
+ transition from => to, :if => lambda { |item|
50
+ opts[:if].map { |method|
51
+ item.send(method)
52
+ }.all?
53
+ }
54
+ #, :do => lambda { |item|
55
+ # opts[:do].each { |method|
56
+ # item.send(method)
57
+ # }
58
+ #}
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ # Before/After transition
65
+ [ :before, :after ].each do |name|
66
+
67
+ (sm[name] || []).each do |opts|
68
+
69
+ from = opts[:from] || :any
70
+ to = opts[:to] || :any
71
+
72
+ from = send(from) if state_aliases.include?(from)
73
+ to = send(to) if state_aliases.include?(to)
74
+
75
+ smc.send(:"#{name}_transition", from => to, :do => lambda { |item|
76
+ opts[:do].each do |method|
77
+ item.send(method)
78
+ end
79
+ })
80
+
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,3 @@
1
+ module Gexp
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper.rb')
3
+
4
+ describe Gexp::Command::Object do
5
+
6
+ context "Команда pick на объекте у себя в локации" do
7
+
8
+ before do
9
+ @user = Fabricate(:user)
10
+ @request = HashWithIndifferentAccess.new({
11
+ :params => {
12
+ :sended_at => 123456789.012,
13
+ :create_at => 123456789.012,
14
+ :commands => [{
15
+ # У себя на карте
16
+ :event => :pick,
17
+ :stage => { x: 100, y: 200 },
18
+ :rewards => { energy: -1, exp: 5 },
19
+ :timestamp => 123456789.012,
20
+ :object => { 'item.stage.house' => '55a55' },
21
+ :transition => { :builded => :builded },
22
+ :seed => 532434234,
23
+ }]
24
+ }
25
+ })
26
+
27
+ @object = Object.new
28
+ @context = Object.new
29
+
30
+ stub(Item::Stage::House).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
36
+
37
+ end
38
+
39
+ it "Команда должна загружать объекты" do
40
+ @command.object.should == @object
41
+ end
42
+
43
+ it "Для конмады определенно событие" do
44
+ @command.event.should == :pick
45
+ end
46
+
47
+ it "Нельзя (так просто взять и) создать команду без параметра-объекта" do
48
+ params = @request[:params][:commands].first
49
+ params.delete(:object)
50
+ lambda {
51
+ @command = Gexp::Command::Object.new params
52
+ }.should raise_error Regexp.new("Can't find object")
53
+ end
54
+
55
+ context "#perform" do
56
+
57
+ it "При успешном выполнении должен вызывать соотвествующее соьытие у объекта" do
58
+ mock(@object).pick.once { true }
59
+ @command.perform
60
+ @command.should be_done
61
+ end
62
+
63
+ context "При неуспешном выполнении" do
64
+
65
+ before do
66
+ mock(@object).pick.once { raise 'Something wrong' }
67
+ end
68
+
69
+ it "Команда должна находится в статусе failed" do
70
+ @command.perform
71
+ @command.should be_failed
72
+ end
73
+
74
+ it "исключение должно агрегироваться в соотвествующем поле" do
75
+ @command.perform
76
+ @command.errors.should_not be_empty
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper.rb')
3
+
4
+ describe Gexp::Command::Stack do
5
+
6
+ context "#each" do
7
+
8
+ let(:request) {
9
+ HashWithIndifferentAccess.new({
10
+ :params => {
11
+ :sended_at => 123456789.012,
12
+ :create_at => 123456789.012,
13
+ :commands => [
14
+ { # У себя на карте
15
+ :event => :pick,
16
+ :stage => { x: 100, y: 200 },
17
+ :rewards => { energy: -1, exp: 5 },
18
+ :timestamp => 123456789.012,
19
+ :objects => [{ 'item.stage.house' => '50c12939584aa5488a000001' }],
20
+ :transition => { :builded => :builded },
21
+ :timestamp => 123456789.010,
22
+ :seed => 532434234,
23
+ }, { # У друга на карте
24
+ :event => :pick,
25
+ :stage => { x: 100, y: 200 },
26
+ :uid => '<friend_uid>',
27
+ :rewards => { energy: -1, exp: 5 },
28
+ :timestamp => 123456789.012,
29
+ :objects => [{ 'item.stage.house' => '50c12939584aa5488a000001' }],
30
+ :transition => { :builded => :builded },
31
+ :timestamp => 123456789.011,
32
+ :seed => 532434234,
33
+ }, { # создание здания
34
+ :event => :create,
35
+ :stage => { x: 100, y: 200 },
36
+ :objects => [{ 'item.stage.house' => '50c12939584aa5488a000001' }],
37
+ :reward => { gold: -100 },
38
+ :timestamp => 123456789.012,
39
+ :seed => 532434234,
40
+ }, { # создание контракта
41
+ :event => :pick,
42
+ :stage => { x: 100, y: 200 },
43
+ :objects => [{ 'item.stage.house' => '50c12939584aa5488a000001' }],
44
+ :timestamp => 123456789.013,
45
+ :seed => 532412334,
46
+ },
47
+ ]
48
+ }
49
+ })
50
+ }
51
+
52
+ before do
53
+ @user = Fabricate(:user)
54
+ @request = request
55
+ end
56
+
57
+ it "Получаем команды из фабрики" do
58
+ @stack = Gexp::Command::Stack.new \
59
+ @user, @request
60
+ @stack.size.should == @request[:params][:commands].size
61
+ end
62
+
63
+ it "Выбрасываем исключение если комманда уже есть" do
64
+ dup_command = @request[:params][:commands].last.dup
65
+ @request[:params][:commands] << dup_command
66
+ lambda {
67
+ @stack = Gexp::Command::Stack.new \
68
+ @user, @request
69
+ }.should raise_error /Command with hash .+ be exist/
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper.rb')
3
+
4
+ describe Gexp::Command do
5
+
6
+ context "Базовый функционал команды" do
7
+
8
+ before do
9
+ @user = Fabricate(:user)
10
+ @request = HashWithIndifferentAccess.new({
11
+ :params => {
12
+ :event => :command_event,
13
+ :timestamp => 1234567890.123,
14
+ :seed => 123321123 }
15
+ })
16
+
17
+ @user = Object.new
18
+ @context = Object.new
19
+
20
+ lambda {
21
+ @command = Gexp::Command.new @request[:params]
22
+ @command.context = @context
23
+ }.should_not raise_error
24
+ end
25
+
26
+
27
+ it "Команде должен выставлятся контекст" do
28
+ @command.context.should == @context
29
+ end
30
+
31
+ it "В команду должны должны копироваться параметры" do
32
+ @command.params.should == @request[:params]
33
+ end
34
+
35
+ it "Команда должны реализовывать StateMachine (положительный сценарий)" do
36
+ @command.should be_new
37
+ @command.activate!
38
+ @command.should be_active
39
+ @command.complete!
40
+ @command.should be_done
41
+ end
42
+
43
+ it "Команда должны реализовывать StateMachine (отрицательный сценарий)" do
44
+ @command.should be_new
45
+ @command.activate!
46
+ @command.should be_active
47
+ @command.failure!
48
+ @command.should be_failed
49
+ end
50
+
51
+ it "Базовую команду нельзя выполнить" do
52
+ lambda {
53
+ @command.perform
54
+ }.should raise_error /Not defined perform method/
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb')
3
+
4
+ describe Gexp::Handler::Check::Item do
5
+
6
+ before do
7
+ @user = User.new
8
+ @object = Object.new
9
+
10
+ subject.user = @user
11
+ subject.object = @object
12
+ end
13
+
14
+ it "Если имеем объект и не передали блок" do
15
+ mock(@user).have_with_type?('item.stage.house', 1) { true }
16
+ result = subject.process({ 'item.stage.house' => 1 })
17
+ result.should be_true
18
+ end
19
+
20
+ it "Если имеем объект передали блок" do
21
+ mock(@user).have_with_type?('item.stage.house', 1) { true }
22
+ result = false
23
+ subject.process({ 'item.stage.house' => 1 }) {
24
+ result = true
25
+ }.should be_true
26
+ result.should be_true
27
+ end
28
+
29
+ it "Если не имеем объект и не передали блок" do
30
+ mock(@user).have_with_type?('item.stage.house', 1) { false }
31
+ lambda {
32
+ subject.process({ 'item.stage.house' => 1 })
33
+ }.should raise_error /not_have_items/
34
+ end
35
+
36
+ it "Если имеем объект передали блок" do
37
+ mock(@user).have_with_type?('item.stage.house', 1) { false }
38
+ result = false
39
+ subject.process({ 'item.stage.house' => 1 }) {
40
+ result = true
41
+ }.should be_nil
42
+ result.should be_false
43
+ end
44
+
45
+ end
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb')
3
+
4
+ describe Gexp::Handler::Check::Resources do
5
+
6
+ before do
7
+ @user = User.new
8
+ @object = Object.new
9
+
10
+ subject.user = @user
11
+ subject.object = @object
12
+ end
13
+
14
+ context "#access_resource" do
15
+
16
+ before {
17
+ pending "Еще не проверялись"
18
+ }
19
+
20
+ it "Возвращение значения ресурса" do
21
+ @user.energy = 5
22
+ energy = subject.chain_resource('energy', @user)
23
+ energy.should == 5
24
+ end
25
+
26
+ it "Возвращение значения вложенного ресурса" do
27
+ @user.resources.resource_1 = 5
28
+ resource_1 = subject.chain_resource('resources.resource_1', @user)
29
+ resource_1.resource_1.should == 5
30
+ end
31
+
32
+ it "Назначение значения ресурса" do
33
+ @user.energy = 5
34
+ subject.chain_resource('energy=', @user, 6)
35
+ @user.energy.should == 6
36
+ end
37
+
38
+ it "Назначение значения ыложенного ресурса" do
39
+ @user.resources.resource_1 = 5
40
+ subject.chain_resource('resources.resource_1=', @user, 6)
41
+ @user.resources.resource_1.should == 6
42
+ end
43
+
44
+ end
45
+
46
+ it "Если у пользователя есть ресурс" do
47
+ @user.energy = 5
48
+ subject.process(energy: 5).should be_true
49
+ end
50
+
51
+ it "Если у пользователя есть ресурсы" do
52
+ @user.energy = 5
53
+ @user.exp = 5
54
+ subject.process(energy: 5, exp: 3).should be_true
55
+ end
56
+
57
+ it "Если у пользователя нет ресурсов" do
58
+ @user.energy = 5
59
+ @user.exp = 5
60
+ lambda {
61
+ subject.process(energy: 6, exp: 7).should be_false
62
+ }.should raise_error /out_of_resources\-energy,exp/
63
+ end
64
+
65
+ it "Успешная проверка ресурсов с блоком" do
66
+ @user.energy = 5
67
+ @user.exp = 5
68
+ prob = false
69
+ subject.process(energy: 5, exp: 3) { prob = true }
70
+ prob.should be_true
71
+ end
72
+
73
+ it "Не успешная проверка ресурсов с блоком" do
74
+ @user.energy = 0
75
+ @user.exp = 5
76
+ prob = false
77
+ subject.process(energy: 5, exp: 3) { prob = true }
78
+ prob.should be_false
79
+ end
80
+
81
+ end