gexp 0.0.1

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