ruby_robot 0.1.9

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