loco_bot 1.0.0

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