diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..2bd750b6 --- /dev/null +++ b/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true +source "https://rubygems.org" + +gem 'rake' +gem 'minitest' +gem 'minitest-spec' +gem 'minitest-reporters' +gem "pry" +gem 'minitest-skip' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..630ea95b --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,36 @@ +GEM + remote: https://rubygems.org/ + specs: + ansi (1.5.0) + builder (3.2.4) + coderay (1.1.3) + method_source (1.0.0) + minitest (5.14.2) + minitest-reporters (1.4.2) + ansi + builder + minitest (>= 5.0) + ruby-progressbar + minitest-skip (0.0.3) + minitest (~> 5.0) + minitest-spec (0.0.2.1) + minitest (>= 3.0) + pry (0.13.1) + coderay (~> 1.1) + method_source (~> 1.0) + rake (13.0.1) + ruby-progressbar (1.10.1) + +PLATFORMS + ruby + +DEPENDENCIES + minitest + minitest-reporters + minitest-skip + minitest-spec + pry + rake + +BUNDLED WITH + 2.1.4 diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..fb007c61 --- /dev/null +++ b/Rakefile @@ -0,0 +1,9 @@ +require 'rake/testtask' + +Rake::TestTask.new do |t| + t.libs = ["lib"] + t.warning = true + t.test_files = FileList['test/*_test.rb'] +end + +task default: :test \ No newline at end of file diff --git a/lib/main.rb b/lib/main.rb new file mode 100644 index 00000000..14cfac3c --- /dev/null +++ b/lib/main.rb @@ -0,0 +1,140 @@ +# Pauline Chane (@PaulineChane) +# Ada Developers Academy C14 +# Solar System Assignment - main.rb Main Class Driver/CLI methods +# 09/23/2020 + +# main.rb +require_relative 'planet' +require_relative 'solar_system' + +# defs for control loop actions that take a little more time +# get detail for planet and account for invalid input +def get_planet_detail(solar_system_obj) + print "Enter name of Planet: " + input = gets.chomp + + # don't exit until name of findable planet is entered + while solar_system_obj.find_planet_by_name(input).nil? do + print "Planet not found in Solar System. Please try again: " + input = gets.chomp + end + + return solar_system_obj.find_planet_by_name(input) +end + +# get and validate user input for adding planet with new info +def user_add_planet(solar_system_obj) + # prompt/input for all input parameters stored in individual variables + print "Enter planet name: " + name = gets.chomp + + print "Enter planet color: " + color = gets.chomp + + # mass must be greater than 0 + print "Enter planet mass (in kg): " + mass = gets.chomp.to_f + + while mass <= 0 do + puts "Invalid mass (in kg) input - must be greater than zero" + print "Enter planet mass (in kg): " + mass = gets.chomp.to_f + end + + # distance from sun must be greater than zero + print "Enter planet's distance from the sun (in km): " + distance_from_sun = gets.chomp.to_f + + while distance_from_sun <= 0 do + puts "Invalid distance from sun (in km) input - must be greater than zero" + print "Enter planet's distance from the sun (in km): " + distance_from_sun = gets.chomp.to_f + end + + print "Enter planet fun fact: " + fun_fact = gets.chomp + + new_planet = Planet.new(name, color, mass, distance_from_sun, fun_fact) + + solar_system_obj.add_planet(new_planet) + return true +end + +# get user input for two planets CURRENTLY in the solar system and return the distance between them +# returns a 3 element array that allows for printing a string stating the distance between two planets. +def user_input_distance(solar_system_obj) + puts "ENTER FIRST PLANET" + planet1 = get_planet_detail(solar_system_obj) + + puts "ENTER SECOND PLANET" + planet2 = get_planet_detail(solar_system_obj) + + return [planet1.name, planet2.name, solar_system_obj.distance_between(planet1.name, planet2.name)] # name of planets and distance between them +end + + +# code for driver itself +def main + # create base solar_system with some planets + solar_system = SolarSystem.new('Sol') + + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + mars = Planet.new('Mars', 'red', 6.4191e23, 2.279e8, 'Planet with the highest known mountain in its solar system - Olympus Mons') + + solar_system.add_planet(earth) + solar_system.add_planet(mars) + + # intro to CLI interface + puts '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+' + puts 'WELCOME TO CLISSIN (Command Line Interface Solar System Info Navigation)!' + puts '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+' + puts '| MENU SELECT |' + puts '|_____Please enter the NUMBER corresponding to your desired action:_____|' + puts '|1. LIST PLANETS CURRENTLY IN SOLAR SYSTEM |' + puts '|2. OBTAIN DETAILS ON A PLANET CURRENTLY IN SOLAR SYSTEM |' + puts '|3. ADD A PLANET TO THE SOLAR SYSTEM |' + puts '|4. FIND THE DISTANCE BETWEEN TWO PLANETS (IF LINED UP IN STRAIGHT LINE)|' + puts '|5. EXIT |' + puts '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+' + print "Enter your selection: " + user_input = gets.chomp.to_i + + while user_input != 5 do + case user_input + when 1 # list planets + puts "---------------------------" + puts "LISTING PLANETS ..." + puts solar_system.list_planets + puts "---------------------------" + when 2 # planet details + puts "---------------------------" + puts "GETTING PLANET DETAILS ..." + planet_det = get_planet_detail(solar_system) + puts planet_det.summary + puts "---------------------------" + when 3 # add planet + puts "---------------------------" + puts "ADDING PLANET ..." + user_add_planet(solar_system) + puts "---------------------------" + when 4 # distance between two planets + puts "---------------------------" + puts "GETTING DISTANCE BETWEEN TWO PLANETS ..." + distance_info = user_input_distance(solar_system) + puts "Assuming they are lined up in a straight line, the distance between %s and %s is %.2f" % distance_info + puts "---------------------------" + when 5 # exit case + break + else # invalid input + print "INVALID INPUT. " + end + puts "CURRENTLY YOU CAN:\nEnter 1 to LIST PLANETS\nEnter 2 to GET PLANET DETAILS\nEnter 3 to ADD A PLANET\nEnter 4 to FIND THE DISTANCE BETWEEN TWO PLANETS\nEnter 5 to EXIT" + print "Select an option: " + user_input = gets.chomp.to_i + end + + puts "Thank you for using CLISSIN. See you Space Cowpoke!" + +end + +main \ No newline at end of file diff --git a/lib/planet.rb b/lib/planet.rb new file mode 100644 index 00000000..58727670 --- /dev/null +++ b/lib/planet.rb @@ -0,0 +1,34 @@ +# Pauline Chane (@PaulineChane) +# Ada Developers Academy C14 +# Solar System Assignment - planet.rb Planet Class +# 09/23/2020 + +# Planet class object +class Planet + # readable access + attr_reader :name, :color, :mass_kg, :distance_from_sun_km, :fun_fact + + # constructor + def initialize(name, color, mass_kg, distance_from_sun_km, fun_fact) + # so since i can't raise an error while assigning a field, + # going to do that here for @mass_kg and @distance_from_sun_km + raise ArgumentError, "Invalid @mass_kg input - must be greater than zero" unless mass_kg > 0 + raise ArgumentError, "Invalid @distance_from_sun_km input - must be greater than zero" unless distance_from_sun_km > 0 + + # assign fields if no errors raised + @name = name + @color = color + @mass_kg = mass_kg + @distance_from_sun_km = distance_from_sun_km + @fun_fact = fun_fact + end + + # addtl accessors/readers + # returns formatted summary of planet info. + def summary + # generally, we don't want to put puts statements in class/instance methods. + # puts doesn't return a string that we could potentially need to use in something else. + # puts will also risk giving us unwanted output in some cases. + return "SUMMARY OF PLANET\nPLANET: #{@name}\nCOLOR: #{@color}\nMASS (KG): #{@mass_kg}\nDISTANCE FROM SUN (KM): #{@distance_from_sun_km}\nFUN FACT: #{@fun_fact}" + end +end diff --git a/lib/solar_system.rb b/lib/solar_system.rb new file mode 100644 index 00000000..d9e15fdd --- /dev/null +++ b/lib/solar_system.rb @@ -0,0 +1,56 @@ +# Pauline Chane (@PaulineChane) +# Ada Developers Academy C14 +# Solar System Assignment - solar_system.rb Solar System Class +# 09/23/2020 + +# Solar System class object +class SolarSystem + # field accessors/readers + attr_reader :star_name, :planets + # constructor + def initialize(star_name) + @star_name = star_name + @planets = [] + end + + # other accessors/readers + # list all planets in @planets array, return as string + def list_planets + planet_list = "Planets orbiting #{@star_name}" + (0...@planets.length).each do |i| + planet_list += "\n#{i + 1}. #{@planets[i].name}" + end + return planet_list + end + + # returns instance of Planet object in @planet that has matching (not case sensitive) planet.name to input string + # if multiple planets have same name in @name field, returns first instance + # if no planets match, returns nil + def find_planet_by_name(planet_name) + return @planets.detect{|planet| planet.name.downcase == planet_name.downcase} + end + + # returns, assuming all planets are in lined up a straight line, distance between two planets (absolute value) + # as noted by two input parameters (two planet names) + # if neither planet is found in SolarSystem object, returns nil + def distance_between(planet_name1, planet_name2) + planet1 = self.find_planet_by_name(planet_name1) + planet2 = self.find_planet_by_name(planet_name2) + + return planet1.nil? || planet2.nil? ? nil : (planet2.distance_from_sun_km - planet1.distance_from_sun_km).abs + end + + # mutators + # add planet to @planets array + # returns true upon successful addition of Planet object + # otherwise, nothing is added and function returns false + def add_planet(planet) + if planet.is_a?(Planet) + @planets << planet + else + return false + end + return true + end + +end \ No newline at end of file diff --git a/test/planet_test.rb b/test/planet_test.rb new file mode 100644 index 00000000..ffb3d798 --- /dev/null +++ b/test/planet_test.rb @@ -0,0 +1,99 @@ +# Pauline Chane (@PaulineChane on GitHub) +# Ada Developers Academy Cohort 14 +# Solar System Optional Exercise - Planet Class Minitests +# 09/23/2020 + +# Practice TDD by writing tests to validate functionality for two new classes: Planet and SolarSystem + +require 'minitest' +require 'minitest/spec' +require 'minitest/autorun' +require 'minitest/reporters' +require 'minitest/pride' + +require_relative '../lib/planet' + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new + +describe 'Planet' do + + it 'creates a new Planet object' do + # Arrange and Act + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + + # Assert + expect(earth).must_be_instance_of Planet + end + + it 'can read all field inputs for a created Planet object' do + # Arrange and Act + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + + # Assert for each field + expect(earth.name).must_equal 'Earth' + expect(earth.color).must_equal 'blue-green' + expect(earth.mass_kg).must_be_close_to 5.972e24 + expect(earth.distance_from_sun_km).must_be_close_to 1.496e8 + expect(earth.fun_fact).must_equal 'Only planet known to support life' + end + + it 'can print a summary of all fields assigned for a Planet object' do + + # Arrange and Act + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + + earth_summary = earth.summary + # Assert <- You do this part! + expect(earth_summary).must_equal "SUMMARY OF PLANET\nPLANET: #{earth.name}\nCOLOR: #{earth.color}\nMASS (KG): #{earth.mass_kg}\nDISTANCE FROM SUN (KM): #{earth.distance_from_sun_km}\nFUN FACT: #{earth.fun_fact}" + end + + it 'raises an ArgumentError for mass_kg construction parameter input less than or equal to 0' do + # error for 0 + expect { + Planet.new('Earth', 'blue-green', 0, 1.496e8, 'Only planet known to support life') # 24 + }.must_raise ArgumentError + # error for less than 0 + expect { + Planet.new('Earth', 'blue-green', -1, 1.496e8, 'Only planet known to support life') # 24 + }.must_raise ArgumentError + end + + it 'raises an ArgumentError for distance_from_sun_km construction parameter input less than or equal to 0' do + # error for 0 + expect { + Planet.new('Earth', 'blue-green', 5.972e24, 0, 'Only planet known to support life') # 24 + }.must_raise ArgumentError + # error for less than 0 + expect { + Planet.new('Earth', 'blue-green', 5.972e24, -1, 'Only planet known to support life') # 24 + }.must_raise ArgumentError + end + + it 'raises an NoMethodError when user attempts to write/mutate Planet fields, ex) planet.color = \'pink\'' do + # Arrange and Act + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + + # Assert + # catch error for attempts to write in all fields as user + expect { + earth.name = "Not Earth" + }.must_raise NoMethodError + + expect { + earth.color = "lava red" + }.must_raise NoMethodError + + expect { + earth.mass_kg = "1000000" + }.must_raise NoMethodError + + expect { + earth.distance_from_sun_km = "1000000" + }.must_raise NoMethodError + + expect { + earth.fun_fact = "Mostly harmless." + }.must_raise NoMethodError + end + +end \ No newline at end of file diff --git a/test/solar_system_test.rb b/test/solar_system_test.rb new file mode 100644 index 00000000..efe97fd8 --- /dev/null +++ b/test/solar_system_test.rb @@ -0,0 +1,108 @@ +# Pauline Chane (@PaulineChane on GitHub) +# Ada Developers Academy Cohort 14 +# Solar System Optional Exercise - SolarSystem Class Minitests +# 09/23/2020 + +# Practice TDD by writing tests to validate functionality for two new classes: Planet and SolarSystem + +require 'minitest' +require 'minitest/spec' +require 'minitest/autorun' +require 'minitest/reporters' +require 'minitest/pride' + +require_relative '../lib/solar_system' +require_relative '../lib/planet' + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new + +describe 'SolarSystem' do + + it 'creates a new Solar System object' do + # Arrange and Act + solar_system = SolarSystem.new('Sol') + + # Assert + expect(solar_system).must_be_instance_of SolarSystem + end + + it 'can add ONLY Planet object to @planets' do + # Arrange and Act + solar_system = SolarSystem.new('Sol') + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + + # Assert for each field + expect(solar_system.add_planet(earth)).must_equal true # adds planet + expect(solar_system.add_planet(["earth"])).must_equal false # doesn't add non-Planet object + end + + it 'can return a string that contains a list of planets in @planets' do + + # Arrange and Act + solar_system = SolarSystem.new('Sol') + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + solar_system.add_planet(earth) + + # Assert <- You do this part! + expect(solar_system.list_planets).must_equal "Planets orbiting #{solar_system.star_name}\n1. #{earth.name}" + end + + it 'can find a planet in the @planets array of a SolarSystem object by the planet\'s name field' do + # Arrange + solar_system = SolarSystem.new('Sol') + earth1 = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + earth2 = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Has humans') + # earth2 verifies that the first occurrence of a planet with name "Earth" is what is returned + + solar_system.add_planet(earth1) + solar_system.add_planet(earth2) + + # Act + found_planet1 = solar_system.find_planet_by_name('Earth') # first occurrence/exact name match + found_planet2 = solar_system.find_planet_by_name('eArTh') # catch cases + found_planet3 = solar_system.find_planet_by_name('mars') # not found, should return nil + + # Assert + expect(found_planet1.fun_fact).must_equal 'Only planet known to support life' + expect(found_planet2.name).must_equal 'Earth' + expect(found_planet3).must_be_nil + end + + it 'can find, assuming all planets are in a straight line, the distance between two planets in a SolarSystem object as input by name' do + # Arrange + solar_system = SolarSystem.new('Sol') + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + mars = Planet.new('Mars', 'red', 6.4191e23, 2.279e8, 'Planet with the highest known mountain in its solar system - Olympus Mons') + + solar_system.add_planet(earth) + solar_system.add_planet(mars) + + # Act + mars_earth_distance = solar_system.distance_between("earth", "mars") # not case sensitive!! + earth_klaatu_nebula_distance = solar_system.distance_between("earth", "klaatu nebula") + + # Assert + expect(mars_earth_distance).must_be_close_to 78300000.0 + expect(earth_klaatu_nebula_distance).must_be_nil + end + + + it 'raises an ArgumentError for attempts to write variables into fields' do + #Arrange and Act + solar_system = SolarSystem.new('Sol') + earth = Planet.new('Earth', 'blue-green', 5.972e24, 1.496e8, 'Only planet known to support life') + + solar_system.add_planet(earth) + + # Assert + # catch error for attempts to write in all fields as user + expect { + solar_system.star_name = "Sokor" + }.must_raise NoMethodError + + expect { + solar_system.planets = [earth] + }.must_raise NoMethodError + end + +end \ No newline at end of file