loco_bot 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +393 -0
  8. data/Rakefile +9 -0
  9. data/bin/loco_bot_cli +10 -0
  10. data/lib/loco_bot/cli/command/base.rb +28 -0
  11. data/lib/loco_bot/cli/command/hodor.rb +14 -0
  12. data/lib/loco_bot/cli/command/left.rb +14 -0
  13. data/lib/loco_bot/cli/command/move.rb +14 -0
  14. data/lib/loco_bot/cli/command/place.rb +17 -0
  15. data/lib/loco_bot/cli/command/report.rb +18 -0
  16. data/lib/loco_bot/cli/command/right.rb +14 -0
  17. data/lib/loco_bot/cli/command.rb +34 -0
  18. data/lib/loco_bot/cli.rb +67 -0
  19. data/lib/loco_bot/robot/direction/east.rb +37 -0
  20. data/lib/loco_bot/robot/direction/north.rb +37 -0
  21. data/lib/loco_bot/robot/direction/south.rb +37 -0
  22. data/lib/loco_bot/robot/direction/west.rb +37 -0
  23. data/lib/loco_bot/robot/direction.rb +27 -0
  24. data/lib/loco_bot/robot.rb +102 -0
  25. data/lib/loco_bot/table.rb +94 -0
  26. data/lib/loco_bot/version.rb +4 -0
  27. data/lib/loco_bot.rb +8 -0
  28. data/loco_bot.gemspec +26 -0
  29. data/spec/loco_bot/cli/command/hodor_spec.rb +14 -0
  30. data/spec/loco_bot/cli/command/left_spec.rb +14 -0
  31. data/spec/loco_bot/cli/command/move_spec.rb +14 -0
  32. data/spec/loco_bot/cli/command/place_spec.rb +14 -0
  33. data/spec/loco_bot/cli/command/report_spec.rb +22 -0
  34. data/spec/loco_bot/cli/command/right_spec.rb +14 -0
  35. data/spec/loco_bot/cli/command_spec.rb +53 -0
  36. data/spec/loco_bot/cli_spec.rb +37 -0
  37. data/spec/loco_bot/robot/direction/east_spec.rb +25 -0
  38. data/spec/loco_bot/robot/direction/north_spec.rb +25 -0
  39. data/spec/loco_bot/robot/direction/south_spec.rb +25 -0
  40. data/spec/loco_bot/robot/direction/west_spec.rb +25 -0
  41. data/spec/loco_bot/robot/direction_spec.rb +35 -0
  42. data/spec/loco_bot/robot_spec.rb +247 -0
  43. data/spec/loco_bot/table_spec.rb +197 -0
  44. data/spec/spec_helper.rb +31 -0
  45. data/spec/support/test_1.txt +3 -0
  46. data/spec/support/test_2.txt +3 -0
  47. data/spec/support/test_3.txt +6 -0
  48. data/spec/support/test_4.txt +28 -0
  49. metadata +183 -0
@@ -0,0 +1,67 @@
1
+ require 'loco_bot/cli/command'
2
+
3
+ module LocoBot
4
+ # Provides a 'command line'-like interface to interact with the API,
5
+ # in a limited way: only one Robot and Table instances are available.
6
+ class CLI
7
+ # The regexp used to parse input.
8
+ INPUT_SPLIT_REGEXP = / |,|\s/
9
+
10
+ # @!attribute [r] table
11
+ # @return [Robot] the Robot
12
+ attr_reader :robot
13
+
14
+ # @!attribute [r] table
15
+ # @return [Table] the Table
16
+ attr_reader :table
17
+
18
+ def initialize
19
+ @robot = Robot.new
20
+ @table = Table.new
21
+ end
22
+
23
+ # Reads input for commands.
24
+ # @param input [String] the input
25
+ # @note Available commands are:
26
+ # - PLACE X,Y,F: put the robot on the table in position X,Y and facing NORTH, SOUTH, EAST or WEST.
27
+ # - MOVE: move the robot one unit forward in the direction it is currently facing.
28
+ # - LEFT and RIGHT rotate the robot 90 degrees in the specified direction without changing its position.
29
+ # - REPORT announces the X,Y and F of the robot.
30
+ # - HODOR outputs a friendly greating.
31
+ # @return [void]
32
+ def input!(input)
33
+ parsed_data = parse_input(input)
34
+
35
+ return if parsed_data[:command].nil?
36
+
37
+ command_class = Command.class_from_name(parsed_data[:command])
38
+
39
+ if command_class.nil?
40
+ puts "#{parsed_data[:command].upcase}: not a known command. Valid commands are #{CLI::Command.list.join(', ')}."
41
+ else
42
+ execute_command(command_class, parsed_data[:arguments])
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def execute_command(command_class, arguments)
49
+ begin
50
+ command_class.new(robot, table).execute(*arguments)
51
+ rescue ArgumentError => exception
52
+ puts "#{command_class.label}: #{exception.message}"
53
+ end
54
+ end
55
+
56
+ def parse_input(input)
57
+ return {} if input.nil?
58
+
59
+ split_data = input.split(INPUT_SPLIT_REGEXP)
60
+ split_data.reject!(&:empty?)
61
+
62
+ return {} if split_data.empty?
63
+
64
+ { command: split_data[0], arguments: split_data.drop(1) }
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,37 @@
1
+ module LocoBot
2
+ class Robot
3
+ module Direction
4
+ # East direction.
5
+ module East
6
+
7
+ # Returns the Direction at the left of this one.
8
+ # @return [Direction::North]
9
+ def self.left
10
+ Direction::North
11
+ end
12
+
13
+ # Returns the Direction at the right of this one.
14
+ # @return [Direction::South]
15
+ def self.right
16
+ Direction::South
17
+ end
18
+
19
+ # Returns the Direction's label.
20
+ # @return [String] the Direction's label
21
+ def self.label
22
+ 'EAST'
23
+ end
24
+
25
+ # Returns the given coordinates in a Hash, modified to reflect the Direction.
26
+ # @param x [Integer] the x-axis coordinate
27
+ # @param y [Integer] the y-axis coordinate
28
+ # @return [Hash] the modified x and y coordinates
29
+ # @example
30
+ # Direction::East.coordinates(1, 2) # => {x: 2, y: 2}
31
+ def self.coordinates(x, y)
32
+ { x: x + 1, y: y }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ module LocoBot
2
+ class Robot
3
+ module Direction
4
+ # North direction.
5
+ module North
6
+
7
+ # Returns the Direction at the left of this one.
8
+ # @return [Direction::West]
9
+ def self.left
10
+ Direction::West
11
+ end
12
+
13
+ # Returns the Direction at the right of this one.
14
+ # @return [Direction::East]
15
+ def self.right
16
+ Direction::East
17
+ end
18
+
19
+ # Returns the Direction's label.
20
+ # @return [String] the Direction's label
21
+ def self.label
22
+ 'NORTH'
23
+ end
24
+
25
+ # Returns the given coordinates in a Hash, modified to reflect the Direction.
26
+ # @param x [Integer] the x-axis coordinate
27
+ # @param y [Integer] the y-axis coordinate
28
+ # @return [Hash] the modified x and y coordinates
29
+ # @example
30
+ # Direction::North.coordinates(1, 2) # => {x: 1, y: 3}
31
+ def self.coordinates(x, y)
32
+ { x: x, y: y + 1 }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ module LocoBot
2
+ class Robot
3
+ module Direction
4
+ # South direction.
5
+ module South
6
+
7
+ # Returns the Direction at the left of this one.
8
+ # @return [Direction::East]
9
+ def self.left
10
+ Direction::East
11
+ end
12
+
13
+ # Returns the Direction at the right of this one.
14
+ # @return [Direction::West]
15
+ def self.right
16
+ Direction::West
17
+ end
18
+
19
+ # Returns the Direction's label.
20
+ # @return [String] the Direction's label
21
+ def self.label
22
+ 'SOUTH'
23
+ end
24
+
25
+ # Returns the given coordinates in a Hash, modified to reflect the Direction.
26
+ # @param x [Integer] the x-axis coordinate
27
+ # @param y [Integer] the y-axis coordinate
28
+ # @return [Hash] the modified x and y coordinates
29
+ # @example
30
+ # Direction::South.coordinates(1, 2) # => {x: 1, y: 1}
31
+ def self.coordinates(x, y)
32
+ { x: x, y: y - 1 }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ module LocoBot
2
+ class Robot
3
+ module Direction
4
+ # West direction.
5
+ module West
6
+
7
+ # Returns the Direction at the left of this one.
8
+ # @return [Direction::South]
9
+ def self.left
10
+ Direction::South
11
+ end
12
+
13
+ # Returns the Direction at the right of this one.
14
+ # @return [Direction::North]
15
+ def self.right
16
+ Direction::North
17
+ end
18
+
19
+ # Returns the Direction's label.
20
+ # @return [String] the Direction's label
21
+ def self.label
22
+ 'WEST'
23
+ end
24
+
25
+ # Returns the given coordinates in a Hash, modified to reflect the Direction.
26
+ # @param x [Integer] the x-axis coordinate
27
+ # @param y [Integer] the y-axis coordinate
28
+ # @return [Hash] the modified x and y coordinates
29
+ # @example
30
+ # Direction::West.coordinates(1, 2) # => {x: 0, y: 2}
31
+ def self.coordinates(x, y)
32
+ { x: x - 1, y: y }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ require 'loco_bot/robot/direction/east'
2
+ require 'loco_bot/robot/direction/north'
3
+ require 'loco_bot/robot/direction/south'
4
+ require 'loco_bot/robot/direction/west'
5
+
6
+ module LocoBot
7
+ class Robot
8
+ # Top-level namespace for the possible facing directions.
9
+ module Direction
10
+
11
+ # Returns the list of available Direction as Strings.
12
+ # @return [Array<String>]
13
+ def self.list
14
+ constants.map(&:to_s)
15
+ end
16
+
17
+ # Returns a Direction class that as the same name as the given name.
18
+ # @param name [String] the Direction name
19
+ # @return [Command] the Direction
20
+ def self.from_name(name)
21
+ if const_defined?(name.capitalize, false)
22
+ const_get(name.capitalize)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,102 @@
1
+ require 'loco_bot/robot/direction'
2
+
3
+ module LocoBot
4
+ # Representation of a robot, placeable on tables.
5
+ class Robot
6
+
7
+ # @!attribute [rw] table
8
+ # @return [Table] the Table the Robot is currently placed on
9
+ attr_accessor :table
10
+
11
+ # @!attribute [rw] x
12
+ # @return [Integer] the x-axis coordinate the Robot is currently at on the Table
13
+ attr_accessor :x
14
+
15
+ # @!attribute [rw] y
16
+ # @return [Integer] the y-axis coordinate the Robot is currently at on the Table
17
+ attr_accessor :y
18
+
19
+ # @!attribute [rw] direction
20
+ # @return [Direction] the Direction the Robot is currently facing on the Table
21
+ attr_accessor :direction
22
+
23
+ # Places the Robot on the given Table at the given coordinates, facing the given Direction.
24
+ # If the Robot was previously on another Table, it is removed from it before being placed on the given.
25
+ # @param table [Table] the Table to place the Robot at
26
+ # @param x [Integer] the x-axis coordinate
27
+ # @param y [Integer] the y-axis coordinate
28
+ # @param direction [Direction] the facing Direction
29
+ # @return [Boolean] true if placing was successful, false otherwise.
30
+ def place(table, x, y, direction)
31
+ table.place_robot(self, x, y, direction)
32
+ end
33
+
34
+ # Removes the Robot its current Table. Position attributes of the Robot are set to nil.
35
+ # @return [Boolean] true if removing was successful, false otherwise.
36
+ def remove
37
+ return false if table.nil?
38
+
39
+ table.remove_robot(self)
40
+ end
41
+
42
+ # Moves the robot one unit forward in the direction it is currently facing.
43
+ # @return [Boolean] true if moving was successful, false otherwise.
44
+ def move
45
+ return false if table.nil? or !table.position_valid?(next_position[:x], next_position[:y])
46
+
47
+ @x = next_position[:x]
48
+ @y = next_position[:y]
49
+
50
+ true
51
+ end
52
+
53
+ # Rotates the robot 90 degrees to the left without changing its position.
54
+ # @return [Boolean] true if turning was successful, false otherwise.
55
+ def turn_left
56
+ return false if table.nil?
57
+
58
+ @direction = direction.left
59
+
60
+ true
61
+ end
62
+
63
+ # Rotates the robot 90 degrees to the right without changing its position.
64
+ # @return [Boolean] true if turning was successful, false otherwise.
65
+ def turn_right
66
+ return false if table.nil?
67
+
68
+ @direction = direction.right
69
+
70
+ true
71
+ end
72
+
73
+ # Returns a Hash containing the x, y and direction of the robot.
74
+ # @return [Hash] the x, y and direction of the Robot.
75
+ # @example
76
+ # robot.place(1, 2, Direction::West)
77
+ # robot.report # => {x: 1, y: 2, direction: LocoBot::Robot::Direction::West}
78
+ def report
79
+ return {} if table.nil?
80
+
81
+ {x: x, y: y, direction: direction}
82
+ end
83
+
84
+ # Outputs a friendly greating.
85
+ # @return [void]
86
+ def hodor!
87
+ puts 'HODOR HODOR !'
88
+ end
89
+
90
+ # Returns a Hash containing the next x and y coordinates of the Robot if it moves facing its current direction.
91
+ # The Hash will be empty if the Robot has not been placed.
92
+ # @return [Hash] the next x and y coordinates of the Robot.
93
+ # @example
94
+ # robot.place(1, 2, Direction::West)
95
+ # robot.next_direction # => {x: 0, y: 2}
96
+ def next_position
97
+ return {} if table.nil?
98
+
99
+ direction.coordinates(x, y)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,94 @@
1
+ module LocoBot
2
+ # Representation of a table on which robots can be placed.
3
+ class Table
4
+
5
+ # @!attribute [r] width
6
+ # @return [Fixnum] width of the Table
7
+ # Determines the farthest accessible point on the Table's x-axis.
8
+ attr_reader :width
9
+
10
+ # @!attribute [r] height
11
+ # @return [Fixnum] height of the table
12
+ # Determines the farthest accessible point on the Table's y-axis.
13
+ attr_reader :height
14
+
15
+ # @!attribute [r] robots
16
+ # @return [Array<Robot>] a collection of [Robot]
17
+ # The collection of Robots currently placed on the Table.
18
+ attr_reader :robots
19
+
20
+ # @param width [Integer] The width of the Table
21
+ # @param height [Integer] The height of the Table
22
+ def initialize(width = 5, height = 5)
23
+ @width = width
24
+ @height = height
25
+ @robots = []
26
+
27
+ validate_dimensions
28
+ end
29
+
30
+ # Places the given Robot on the Table at the given coordinates, facing the given Direction.
31
+ # If the Robot was previously on another Table, it is removed from it before being placed on the current.
32
+ # @param robot [Robot] the Robot to place on the Table
33
+ # @param x [Integer] the x-axis coordinate
34
+ # @param y [Integer] the y-axis coordinate
35
+ # @param direction [Direction] the facing Direction
36
+ # @return [Boolean] true if placing was successful, false otherwise.
37
+ def place_robot(robot, x, y, direction)
38
+ return false unless position_valid?(x, y)
39
+
40
+ robot.table.remove_robot(robot) if robot.table
41
+
42
+ robot.table = self
43
+ robot.direction = direction
44
+ robot.x = x
45
+ robot.y = y
46
+
47
+ @robots.push(robot) unless robots.include?(robot)
48
+
49
+ true
50
+ end
51
+
52
+ # Removes the given Robot from the Table. Position attributes of the Robot are set to nil
53
+ # @param robot [Robot] the Robot to remove from the Table
54
+ # @return [Boolean] true if removing was successful, false otherwise.
55
+ def remove_robot(robot)
56
+ return false unless robots.include?(robot)
57
+
58
+ robot.table = nil
59
+ robot.x = nil
60
+ robot.y = nil
61
+ robot.direction = nil
62
+
63
+ @robots.delete(robot)
64
+
65
+ true
66
+ end
67
+
68
+ # Determines whether a Robot can be placed at the given coordinates.
69
+ # @param x [Integer] the x-axis coordinate
70
+ # @param y [Integer] the y-axis coordinate
71
+ # @return [Boolean] true if a Robot can be placed at the given coordinates, false otherwise.
72
+ def position_valid?(x, y)
73
+ position_within_bounds?(x, y) and position_free?(x, y)
74
+ end
75
+
76
+ private
77
+
78
+ def position_within_bounds?(x, y)
79
+ x >= 0 && y >= 0 && x < width && y < height
80
+ end
81
+
82
+ def position_free?(x, y)
83
+ robots.none? {|robot| robot.x == x && robot.y == y }
84
+ end
85
+
86
+ def validate_dimensions
87
+ errors = []
88
+ errors << "#{width} is not a valid width: it must be > 0" unless width > 0
89
+ errors << "#{height} is not a valid height: it must be > 0" unless height > 0
90
+
91
+ raise ArgumentError.new(errors.join(', ')) unless errors.empty?
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,4 @@
1
+ module LocoBot
2
+ # The gem's current version
3
+ VERSION = '1.0.0'
4
+ end
data/lib/loco_bot.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'loco_bot/cli'
2
+ require 'loco_bot/robot'
3
+ require 'loco_bot/table'
4
+ require 'loco_bot/version'
5
+
6
+ # Top-level namespace of the gem.
7
+ module LocoBot
8
+ end
data/loco_bot.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'loco_bot/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'loco_bot'
8
+ spec.version = LocoBot::VERSION
9
+ spec.authors = ['Rafaël Gonzalez']
10
+ spec.email = ['github@rafaelgonzalez.me']
11
+ spec.summary = %q{Ruby gem of crazy robots and benevolent tables that keep watching over them.}
12
+ spec.description = %q{Issue commands to control the robots and the tables will keep you from making them fall, you monster!}
13
+ spec.homepage = 'https://github.com/rafaelgonzalez/loco_bot'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.6'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec', '~> 3.0'
24
+ spec.add_development_dependency 'simplecov', '~> 0.9'
25
+ spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.0'
26
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe LocoBot::CLI::Command::Hodor do
2
+ let(:robot) { LocoBot::Robot.new }
3
+ let(:table) { LocoBot::Table.new }
4
+
5
+ subject { described_class.new(robot, table) }
6
+
7
+ describe '#execute' do
8
+ it 'calls #hodor! on robot' do
9
+ expect(subject.robot).to receive(:hodor!).with(no_args).once
10
+
11
+ subject.execute
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe LocoBot::CLI::Command::Left do
2
+ let(:robot) { LocoBot::Robot.new }
3
+ let(:table) { LocoBot::Table.new }
4
+
5
+ subject { described_class.new(robot, table) }
6
+
7
+ describe '#execute' do
8
+ it 'calls #turn_left on robot' do
9
+ expect(subject.robot).to receive(:turn_left).with(no_args).once
10
+
11
+ subject.execute
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe LocoBot::CLI::Command::Move do
2
+ let(:robot) { LocoBot::Robot.new }
3
+ let(:table) { LocoBot::Table.new }
4
+
5
+ subject { described_class.new(robot, table) }
6
+
7
+ describe '#execute' do
8
+ it 'calls #move on robot' do
9
+ expect(subject.robot).to receive(:move).with(no_args).once
10
+
11
+ subject.execute
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe LocoBot::CLI::Command::Place do
2
+ let(:robot) { LocoBot::Robot.new }
3
+ let(:table) { LocoBot::Table.new }
4
+
5
+ subject { described_class.new(robot, table) }
6
+
7
+ describe '#execute' do
8
+ it 'calls #place on robot' do
9
+ expect(subject.robot).to receive(:place).with(table, 3, 2, LocoBot::Robot::Direction::West).once
10
+
11
+ subject.execute('3', '2', 'west')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ RSpec.describe LocoBot::CLI::Command::Report do
2
+ let(:robot) { LocoBot::Robot.new }
3
+ let(:table) { LocoBot::Table.new }
4
+
5
+ subject { described_class.new(robot, table) }
6
+
7
+ describe '#execute' do
8
+ context 'before placing robot' do
9
+ it 'does not output a string' do
10
+ expect { subject.execute }.not_to output.to_stdout
11
+ end
12
+ end
13
+
14
+ context 'after placing robot' do
15
+ before { subject.robot.place(table, 4 ,2 , LocoBot::Robot::Direction::East) }
16
+
17
+ it 'outputs a string' do
18
+ expect { subject.execute }.to output("4,2,EAST\n").to_stdout
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe LocoBot::CLI::Command::Right do
2
+ let(:robot) { LocoBot::Robot.new }
3
+ let(:table) { LocoBot::Table.new }
4
+
5
+ subject { described_class.new(robot, table) }
6
+
7
+ describe '#execute' do
8
+ it 'calls #turn_right on robot' do
9
+ expect(subject.robot).to receive(:turn_right).with(no_args).once
10
+
11
+ subject.execute
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+ RSpec.describe LocoBot::CLI::Command do
2
+ describe '.list' do
3
+ subject { described_class.list }
4
+
5
+ it { is_expected.to eql ['HODOR', 'LEFT', 'MOVE', 'PLACE', 'REPORT', 'RIGHT'] }
6
+ end
7
+
8
+ describe '.class_from_name' do
9
+ subject { described_class.class_from_name(direction) }
10
+
11
+ context 'passing :base' do
12
+ let(:direction) { :base }
13
+
14
+ it { is_expected.to eql nil }
15
+ end
16
+
17
+ context 'passing :hodor' do
18
+ let(:direction) { :hodor }
19
+
20
+ it { is_expected.to eql LocoBot::CLI::Command::Hodor }
21
+ end
22
+
23
+ context 'passing :left' do
24
+ let(:direction) { :left }
25
+
26
+ it { is_expected.to eql LocoBot::CLI::Command::Left }
27
+ end
28
+
29
+ context 'passing :move' do
30
+ let(:direction) { :move }
31
+
32
+ it { is_expected.to eql LocoBot::CLI::Command::Move }
33
+ end
34
+
35
+ context 'passing :place' do
36
+ let(:direction) { :place }
37
+
38
+ it { is_expected.to eql LocoBot::CLI::Command::Place }
39
+ end
40
+
41
+ context 'passing :report' do
42
+ let(:direction) { :report }
43
+
44
+ it { is_expected.to eql LocoBot::CLI::Command::Report }
45
+ end
46
+
47
+ context 'passing :right' do
48
+ let(:direction) { :right }
49
+
50
+ it { is_expected.to eql LocoBot::CLI::Command::Right }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,37 @@
1
+ RSpec.describe LocoBot::CLI do
2
+ describe '#initialize' do
3
+ subject { described_class.new }
4
+
5
+ it { expect(subject.robot).to be_an_instance_of(LocoBot::Robot) }
6
+ it { expect(subject.table).to be_an_instance_of(LocoBot::Table) }
7
+ end
8
+
9
+ describe '#input!' do
10
+ let(:input) { "#{command} #{arguments}" }
11
+
12
+ context 'using a valid command' do
13
+ let(:command) { :place }
14
+
15
+ context 'with a valid number of arguments' do
16
+ let(:arguments) { '1, 3, SOUTH' }
17
+ end
18
+
19
+ context 'with an invalid number of arguments' do
20
+ let(:arguments) { '1, 2' }
21
+
22
+ it 'outputs a custom ArgumentError message' do
23
+ expect { subject.input!(input) }.to output("PLACE: wrong number of arguments (2 for 3)\n").to_stdout
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'using an invalid command' do
29
+ let(:command) { :yadayada }
30
+ let(:arguments) { '' }
31
+
32
+ it 'does not raise an error' do
33
+ expect { subject.input!(input) }.to output("YADAYADA: not a known command. Valid commands are HODOR, LEFT, MOVE, PLACE, REPORT, RIGHT.\n").to_stdout
34
+ end
35
+ end
36
+ end
37
+ end