ruby_robot 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +133 -0
  5. data/Guardfile +77 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +50 -0
  8. data/Rakefile +24 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/exe/ruby_robot +34 -0
  12. data/exe/ruby_robot_grpc_client +53 -0
  13. data/exe/ruby_robot_grpc_server +46 -0
  14. data/exe/ruby_robot_web +19 -0
  15. data/json_schema/request.schema.json +37 -0
  16. data/json_schema/response.schema.json +24 -0
  17. data/lib/public/favicon-16x16.png +0 -0
  18. data/lib/public/favicon-32x32.png +0 -0
  19. data/lib/public/index.html +95 -0
  20. data/lib/public/swagger-ui-bundle.js +93 -0
  21. data/lib/public/swagger-ui-standalone-preset.js +13 -0
  22. data/lib/public/swagger-ui.css +3 -0
  23. data/lib/public/swagger-ui.js +8 -0
  24. data/lib/public/swagger.json +179 -0
  25. data/lib/ruby_robot.rb +16 -0
  26. data/lib/ruby_robot/cacerts.crt +92 -0
  27. data/lib/ruby_robot/construction_error.rb +4 -0
  28. data/lib/ruby_robot/grpc/robot_service.rb +102 -0
  29. data/lib/ruby_robot/grpc/ruby_robot_services_pb.rb +34 -0
  30. data/lib/ruby_robot/grpc_helper.rb +15 -0
  31. data/lib/ruby_robot/grpc_ruby/ruby_robot_pb.rb +36 -0
  32. data/lib/ruby_robot/grpc_shell.rb +85 -0
  33. data/lib/ruby_robot/netflix_tabletop.rb +14 -0
  34. data/lib/ruby_robot/placement_error.rb +4 -0
  35. data/lib/ruby_robot/robot.rb +71 -0
  36. data/lib/ruby_robot/ruby_robot.proto +53 -0
  37. data/lib/ruby_robot/schema_loader.rb +10 -0
  38. data/lib/ruby_robot/shell.rb +204 -0
  39. data/lib/ruby_robot/tabletop.rb +120 -0
  40. data/lib/ruby_robot/version.rb +3 -0
  41. data/lib/ruby_robot/webapp.rb +263 -0
  42. data/ruby_robot.gemspec +48 -0
  43. metadata +258 -0
data/lib/ruby_robot.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "ruby_robot/version"
2
+ require "ruby_robot/tabletop"
3
+ require "ruby_robot/netflix_tabletop"
4
+ require "ruby_robot/robot"
5
+ require "ruby_robot/construction_error"
6
+ require "ruby_robot/schema_loader"
7
+ require "ruby_robot/placement_error"
8
+ require "ruby_robot/shell"
9
+ require "ruby_robot/webapp"
10
+ require "ruby_robot/grpc_helper"
11
+ require "ruby_robot/grpc_shell"
12
+ require "ruby_robot/grpc_ruby/ruby_robot_pb"
13
+ require 'ruby_robot/grpc_helper'
14
+
15
+ module RubyRobot
16
+ end
@@ -0,0 +1,92 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
3
+ MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
4
+ IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
5
+ MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
6
+ FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
7
+ bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
8
+ dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
9
+ H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
10
+ uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
11
+ mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
12
+ a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
13
+ E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
14
+ WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
15
+ VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
16
+ Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
17
+ cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
18
+ IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
19
+ AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
20
+ YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
21
+ 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
22
+ Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
23
+ c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
24
+ mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
25
+ -----END CERTIFICATE-----
26
+ -----BEGIN CERTIFICATE-----
27
+ MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
28
+ MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
29
+ ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
30
+ eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
31
+ gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
32
+ BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
33
+ VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
34
+ hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
35
+ AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
36
+ 2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
37
+ ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
38
+ 4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
39
+ m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
40
+ vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
41
+ 8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
42
+ IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
43
+ KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
44
+ GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
45
+ s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
46
+ JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
47
+ AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
48
+ MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
49
+ bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
50
+ Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
51
+ zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
52
+ Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
53
+ Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
54
+ B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
55
+ PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
56
+ pu/xO28QOG8=
57
+ -----END CERTIFICATE-----
58
+ -----BEGIN CERTIFICATE-----
59
+ MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
60
+ hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
61
+ A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
62
+ BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy
63
+ MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
64
+ EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
65
+ Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh
66
+ bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
67
+ ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh
68
+ bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0
69
+ Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6
70
+ ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51
71
+ UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n
72
+ c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY
73
+ MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz
74
+ 30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
75
+ HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG
76
+ BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
77
+ bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB
78
+ AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E
79
+ T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v
80
+ ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p
81
+ mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/
82
+ e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps
83
+ P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY
84
+ dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc
85
+ 2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG
86
+ V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4
87
+ HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX
88
+ j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII
89
+ 0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap
90
+ lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf
91
+ +AZxAeKCINT+b72x
92
+ -----END CERTIFICATE-----
@@ -0,0 +1,4 @@
1
+ module RubyRobot
2
+ class ConstructionError < RuntimeError
3
+ end
4
+ end
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+ require 'grpc'
3
+ # Tweak the load path so ruby_robot_services_pb can
4
+ # load ruby_robot_pb
5
+ $: << File.dirname(__FILE__) + '/../grpc_ruby'
6
+ require 'ruby_robot/grpc/ruby_robot_services_pb'
7
+ require 'logger'
8
+ $:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
9
+ require 'ruby_robot'
10
+
11
+ module RubyRobot
12
+ module Grpc
13
+ class RobotService < ::RubyRobot::RubyRobot::Service
14
+ def logger
15
+ @logger ||= proc {
16
+ Logger.new(STDOUT)
17
+ }.call
18
+ end
19
+
20
+ def robot
21
+ @@robot ||= proc {
22
+ rr = ::RubyRobot::Shell.new
23
+ def rr.placed?
24
+ !@robot.nil?
25
+ end
26
+ rr
27
+ }.call
28
+ end
29
+
30
+ def not_placed_error
31
+ ::RubyRobot::RubyRobotResponse.new(
32
+ error: ::RubyRobot::RubyRobotError.new(error: 400, message: "Robot is not currently placed")
33
+ )
34
+ end
35
+
36
+ def position_report
37
+ rr = robot.REPORT(false)
38
+
39
+ ::RubyRobot::RubyRobotResponse.new(
40
+ current_state: ::RubyRobot::RubyRobotRequest.new(
41
+ x: rr[:x],
42
+ y: rr[:y],
43
+ direction: rr[:direction].to_s.upcase.to_sym
44
+ )
45
+ )
46
+ end
47
+
48
+ def left(empty, _unused_call)
49
+ logger.info "#{__method__} called (robot.placed?==#{robot.placed?})"
50
+ if robot.placed?
51
+ robot.LEFT
52
+ position_report
53
+ else
54
+ not_placed_error
55
+ end
56
+ end
57
+
58
+ def move(empty, _unused_call)
59
+ logger.info "#{__method__} called (robot.placed?==#{robot.placed?})"
60
+ if robot.placed?
61
+ robot.MOVE
62
+ position_report
63
+ else
64
+ not_placed_error
65
+ end
66
+ end
67
+
68
+ def place(robot_request, _unused_call)
69
+ input = { x: robot_request.x, y: robot_request.y, direction: robot_request.direction }
70
+ logger.info "#{__method__} called with #{input.to_json}"
71
+
72
+ robot.PLACE(robot_request.x, robot_request.y, robot_request.direction.to_s.downcase)
73
+ position_report
74
+ end
75
+
76
+ def remove(empty, _unused_call)
77
+ logger.info "#{__method__} called"
78
+ @@robot = nil
79
+ ::Google::Protobuf::Empty.new
80
+ end
81
+
82
+ def report(empty, _unused_call)
83
+ logger.info "#{__method__} called (robot.placed?==#{robot.placed?})"
84
+ if robot.placed?
85
+ position_report
86
+ else
87
+ not_placed_error
88
+ end
89
+ end
90
+
91
+ def right(empty, _unused_call)
92
+ logger.info "#{__method__} called (robot.placed?==#{robot.placed?})"
93
+ if robot.placed?
94
+ robot.RIGHT
95
+ position_report
96
+ else
97
+ not_placed_error
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,34 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: ruby_robot.proto for package 'RubyRobot'
3
+ # Original file comments:
4
+ #
5
+ # gRPC service definition for the RubyRobot.
6
+ #
7
+
8
+ require 'grpc'
9
+ require 'ruby_robot_pb'
10
+
11
+ module RubyRobot
12
+ module RubyRobot
13
+ #
14
+ # Service definitions
15
+ #
16
+ class Service
17
+
18
+ include GRPC::GenericService
19
+
20
+ self.marshal_class_method = :encode
21
+ self.unmarshal_class_method = :decode
22
+ self.service_name = 'RubyRobot.RubyRobot'
23
+
24
+ rpc :Left, Google::Protobuf::Empty, RubyRobotResponse
25
+ rpc :Move, Google::Protobuf::Empty, RubyRobotResponse
26
+ rpc :Place, RubyRobotRequest, RubyRobotResponse
27
+ rpc :Remove, Google::Protobuf::Empty, Google::Protobuf::Empty
28
+ rpc :Report, Google::Protobuf::Empty, RubyRobotResponse
29
+ rpc :Right, Google::Protobuf::Empty, RubyRobotResponse
30
+ end
31
+
32
+ Stub = Service.rpc_stub_class
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ require 'ruby_robot/grpc_ruby/ruby_robot_pb'
2
+ #
3
+ # Define #to_json on ::RubyRobot::RubyRobotRequest for prettier printing.
4
+ #
5
+ module RubyRobot
6
+ class RubyRobotRequest
7
+ def to_json
8
+ {
9
+ x: self.x,
10
+ y: self.y,
11
+ direction: self.direction.to_s
12
+ }.to_json
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: ruby_robot.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ require 'google/protobuf/empty_pb'
7
+ Google::Protobuf::DescriptorPool.generated_pool.build do
8
+ add_message "RubyRobot.RubyRobotRequest" do
9
+ optional :x, :int32, 1
10
+ optional :y, :int32, 2
11
+ optional :direction, :enum, 3, "RubyRobot.RubyRobotRequest.Direction"
12
+ end
13
+ add_enum "RubyRobot.RubyRobotRequest.Direction" do
14
+ value :NORTH, 0
15
+ value :EAST, 1
16
+ value :SOUTH, 2
17
+ value :WEST, 3
18
+ end
19
+ add_message "RubyRobot.RubyRobotError" do
20
+ optional :error, :int32, 1
21
+ optional :message, :string, 2
22
+ end
23
+ add_message "RubyRobot.RubyRobotResponse" do
24
+ oneof :response do
25
+ optional :current_state, :message, 1, "RubyRobot.RubyRobotRequest"
26
+ optional :error, :message, 2, "RubyRobot.RubyRobotError"
27
+ end
28
+ end
29
+ end
30
+
31
+ module RubyRobot
32
+ RubyRobotRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("RubyRobot.RubyRobotRequest").msgclass
33
+ RubyRobotRequest::Direction = Google::Protobuf::DescriptorPool.generated_pool.lookup("RubyRobot.RubyRobotRequest.Direction").enummodule
34
+ RubyRobotError = Google::Protobuf::DescriptorPool.generated_pool.lookup("RubyRobot.RubyRobotError").msgclass
35
+ RubyRobotResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("RubyRobot.RubyRobotResponse").msgclass
36
+ end
@@ -0,0 +1,85 @@
1
+ require 'bombshell'
2
+ require 'logger'
3
+ require 'ruby_robot/shell'
4
+ require 'ruby_robot/grpc_helper'
5
+ $: << File.dirname(__FILE__) + '/grpc_ruby'
6
+ require 'ruby_robot/grpc/ruby_robot_services_pb'
7
+
8
+ module RubyRobot
9
+ #
10
+ # Override all methods inherited from Shell
11
+ # such that they use gRPC to communicate
12
+ # with a remote Robot.
13
+ #
14
+ # TODO: RPC error handling (more info at
15
+ # https://grpc.io/docs/guides/error.html).
16
+ #
17
+ class GrpcShell < ::Bombshell::Environment
18
+ include ::Bombshell::Shell
19
+
20
+ prompt_with 'ILoveNetflixStudio&gRPC'
21
+
22
+ # TODO: Figure out if there's a way to pass args down into
23
+ # ::Bombshell#launch so this isn't a class attr.
24
+ def self.remote_url=(new_url)
25
+ # Convert to URI if String
26
+ @@remote_url = new_url.kind_of?(String) ? URI.parse(new_url) : new_url
27
+ end
28
+ def initialize
29
+ # TODO: Enable SSL/TLS
30
+ # Totally cheating here: if URI is grpc://netflix.avilla.net,
31
+ # then load up the cert (stored in the gem).
32
+ if @@remote_url.to_s.start_with?("grpcs://netflix.avilla.net")
33
+ ca_path = File.join(File.dirname(__FILE__), "cacerts.crt")
34
+ channel_creds = GRPC::Core::ChannelCredentials.new(File.read(ca_path))
35
+ stub_opts = {
36
+ :creds => channel_creds,
37
+ GRPC::Core::Channel::SSL_TARGET => @@remote_url.host
38
+ }
39
+ @stub = ::RubyRobot::RubyRobot::Stub.new(
40
+ "#{@@remote_url.host}:#{@@remote_url.port}", channel_creds
41
+ )
42
+ else
43
+ @stub = ::RubyRobot::RubyRobot::Stub.new("#{@@remote_url.host}:#{@@remote_url.port}", :this_channel_is_insecure)
44
+ end
45
+ puts "Checking state of Robot at #{@@remote_url}"
46
+ self.REPORT()
47
+ end
48
+
49
+ def PLACE(x, y, direction)
50
+ @stub.place(::RubyRobot::RubyRobotRequest.new(x: x, y: y, direction: direction.to_s.upcase.to_sym))
51
+ end
52
+
53
+ def MOVE
54
+ @stub.move(Google::Protobuf::Empty.new)
55
+ end
56
+
57
+ def LEFT
58
+ @stub.left(Google::Protobuf::Empty.new)
59
+ end
60
+
61
+ def RIGHT
62
+ @stub.right(Google::Protobuf::Empty.new)
63
+ end
64
+
65
+ def REPORT(to_stderr=false)
66
+ resp = @stub.report(Google::Protobuf::Empty.new)
67
+ if resp.error
68
+ puts resp.error.message
69
+ else
70
+ puts resp.current_state.to_json
71
+ end
72
+ end
73
+
74
+ def REMOVE
75
+ @stub.remove(Google::Protobuf::Empty.new)
76
+ end
77
+
78
+ alias :QUIT :quit
79
+ end
80
+ end
81
+ module Bombshell
82
+ module Shell
83
+ REMOVE=:remove
84
+ end
85
+ end
@@ -0,0 +1,14 @@
1
+ require 'ruby_robot/tabletop'
2
+
3
+ #
4
+ # A Tabletop which is 5x5
5
+ #
6
+ module RubyRobot
7
+ class NetflixTabletop < Tabletop
8
+ def initialize
9
+ # The instructions say that a Netflix Tabletop
10
+ # is 5x5
11
+ super(5, 5)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ module RubyRobot
2
+ class PlacementError < RuntimeError
3
+ end
4
+ end
@@ -0,0 +1,71 @@
1
+ module RubyRobot
2
+ class Robot
3
+
4
+ # Directions are clockwise from north
5
+ VALID_DIRECTIONS = [:north, :east, :south, :west]
6
+
7
+ attr_reader :direction
8
+ attr_reader :tabletop
9
+
10
+ def initialize(direction)
11
+ orig_direction = direction
12
+ err_msg = "New Robots must have direction value of one of the following symbols: #{VALID_DIRECTIONS.join(', ')}; invalid value '#{orig_direction}'"
13
+ direction = direction.kind_of?(String) ? direction.downcase.to_sym : direction
14
+ raise ConstructionError.new(err_msg) unless VALID_DIRECTIONS.include?(direction)
15
+ @direction = direction
16
+ @tabletop = nil
17
+ end
18
+
19
+ def inspect
20
+ case @direction
21
+ when :north then "^"
22
+ when :south then "|"
23
+ when :east then ">"
24
+ # :west
25
+ else "<"
26
+ end
27
+ end
28
+
29
+ def left
30
+ # A little cheating here...index -1 will effectively wrap around and
31
+ # return the last element
32
+ @direction = VALID_DIRECTIONS[dir_idx - 1]
33
+ end
34
+
35
+ def right
36
+ @direction = VALID_DIRECTIONS[(dir_idx + 1) % VALID_DIRECTIONS.size]
37
+ end
38
+
39
+ #
40
+ # Called by a Tabletop where this has been placed
41
+ #
42
+ def place(tabletop)
43
+ @tabletop = tabletop
44
+ end
45
+
46
+ def report
47
+ @tabletop.position(self).merge(direction: direction)
48
+ end
49
+
50
+ #
51
+ # TODO: Error checking for if @tabletop.nil?
52
+ # Also, @tabletop.move and @tabletop.move? together
53
+ # should be synchronized if multithreaded where > 1
54
+ # Robot are on a Tabletop.
55
+ #
56
+ # Return #report after call, whether it was successful
57
+ # or not (assuming it _is_ in fact placed on a board).
58
+ #
59
+ def move
60
+ @tabletop.move(self, direction) if @tabletop.move?(self, direction)
61
+ report
62
+ end
63
+
64
+ private
65
+
66
+ def dir_idx
67
+ VALID_DIRECTIONS.index(@direction)
68
+ end
69
+
70
+ end
71
+ end