Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (3.2.3)
i18n (~> 0.6)
multi_json (~> 1.0)
diff-lcs (1.1.3)
geocoder (1.1.1)
i18n (0.6.0)
multi_json (1.3.4)
rake (0.9.2.2)
rspec (2.10.0)
rspec-core (~> 2.10.0)
rspec-expectations (~> 2.10.0)
rspec-mocks (~> 2.10.0)
rspec-core (2.10.0)
rspec-expectations (2.10.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.10.1)
activesupport (4.0.2)
i18n (~> 0.6, >= 0.6.4)
minitest (~> 4.2)
multi_json (~> 1.3)
thread_safe (~> 0.1)
tzinfo (~> 0.3.37)
atomic (1.1.14)
diff-lcs (1.2.5)
geocoder (1.1.9)
i18n (0.6.9)
minitest (4.7.5)
multi_json (1.8.4)
rake (10.1.1)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.7)
rspec-expectations (2.14.4)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.4)
thread_safe (0.1.3)
atomic
tzinfo (0.3.38)

PLATFORMS
ruby
Expand Down
11 changes: 6 additions & 5 deletions lib/calculates_route.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
class CalculatesRoute

def self.calculate(points)

remaining_points = points
route = []
route << remaining_points.slice!(0)
route << {point: remaining_points.slice!(0), distance: 0}
until remaining_points == [] do
next_point = shortest_distance(route.last, remaining_points)
route << remaining_points.slice!(remaining_points.index(next_point))
next_point = shortest_distance(route.last.fetch(:point), remaining_points)
remaining_points.delete(next_point.fetch(:point))
route << next_point
end
route
end
Expand All @@ -16,7 +16,8 @@ def self.shortest_distance(from, possible)
distances = possible.map do |point|
{point: point, distance: Map.distance_between(from, point)}
end
distances.sort{|a,b| a.fetch(:distance) <=> b.fetch(:distance)}.first.fetch(:point)
sorted = distances.sort{|a,b| a.fetch(:distance) <=> b.fetch(:distance)}
sorted.first
end
end

9 changes: 5 additions & 4 deletions lib/place.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
require_relative "./map"

class Place
attr_accessor :name, :coordinates
attr_accessor :address, :coordinates, :name

def self.build(name)
results = Map.search(name)
def self.build(address, options= {})
results = Map.search(address)
Place.new.tap do |p|
p.name = name
p.address = address
p.coordinates = results.coordinates
p.name = options[:name].nil? ? address : options[:name]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can set a default value for a hash.fetch -- so you could do the following. If there's a name, it'll use it. Otherwise, use an address.

p.name = options.fetch(:name, address)

end
end

Expand Down
25 changes: 24 additions & 1 deletion lib/sales_person.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
require_relative 'place'

class SalesPerson

attr_reader :cities

def initialize
@cities = []
end
Expand All @@ -9,7 +12,27 @@ def schedule_city(city)
@cities << city unless @cities.include?(city)
end

def route
def route(starting_point_name = nil)
move_starting_point(starting_point_name) if starting_point_name
CalculatesRoute.calculate(cities)
end

def calculate_total_miles(route)
route.map {|leg| leg.fetch(:distance)}.reduce(:+)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

excellent use of reduce 🤘

end

def calculate_traveling_time(total_miles, speed = 55)
total_miles/speed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if you want to handle if speed is 0? It all depends on how you want this to blow up (or return infinite traveling time).

end

private

def find_starting_point(starting_point_name)
@cities.bsearch {|place| place.name.downcase == starting_point_name.downcase }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

binary search, fancy! Why use that here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was me looking at docs trying to find a way to search an array, quickly. Is there a better way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try the exact same syntax for the block, but use "find"

Pretty much the same thing, but less confusion for future readers.

On Mon, Jan 27, 2014 at 12:58 PM, noahpatterson [email protected]
wrote:

 CalculatesRoute.calculate(cities)

end
+

  • def calculate_total_miles(route)
  • route.map {|leg| leg.fetch(:distance)}.reduce(:+)
  • end
  • def calculate_traveling_time(total_miles, speed = 55)
  • total_miles/speed
  • end
  • private
  • def find_starting_point(starting_point_name)
  • @cities.bsearch {|place| place.name.downcase == starting_point_name.downcase }
    This was me looking at docs trying to find a way to search an array, quickly. Is there a better way?

Reply to this email directly or view it on GitHub:
https://github.com/RubyoffRails/Episode7/pull/11/files#r9200786

end

def move_starting_point(starting_point_name)
starting_point = find_starting_point(starting_point_name)
@cities.unshift(@cities.delete(starting_point))
end
end
61 changes: 55 additions & 6 deletions salesperson.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,59 @@
require 'benchmark'

Dir["./lib/*.rb"].each {|file| require file }

@all_cities = ["Abilene", "Alamo", "Alamo Heights", "Alice", "Allen", "Alpine", "Alvarado", "Alvin", "Amarillo", "Andrews", "Angleton", "Argyle", "Arlington", "Austin", "Azle", "Balch Springs", "Bastrop", "Bay City", "Baytown", "Beaumont", "Bellaire", "Belton", "Benbrook", "Big Spring", "Boerne", "Brenham", "Bridgeport", "Brownfield", "Brownsville", "Brownwood", "Bryan", "Bulverde", "Burkburnett", "Burleson", "Burnet", "Canyon", "Carrollton", "Carthage", "Cedar Hill", "Cedar Park", "Cleburne", "Clute", "College Station", "Colleyville", "Columbus", "Conroe", "Converse", "Coppell", "Copperas Cove", "Corinth", "Corpus Christi", "Corsicana", "Crockett", "Crowley", "Dallas", "Deer Park", "Del Rio", "Denison", "Denton", "Desoto", "Devine", "Dickinson", "Donna", "Dumas", "Duncanville", "Eagle Pass", "Edinburg", "El Campo", "El Paso", "Ennis", "Euless", "Farmers Branch", "Flower Mound", "Fort Stockton", "Fort Worth", "Fredericksburg", "Freeport", "Friendswood", "Frisco", "Gainesville", "Galveston", "Garden Ridge", "Garland", "Gatesville", "Georgetown", "Gonzales", "Granbury", "Grand Prairie", "Granite Shoals", "Grapevine", "Greenville", "Haltom City", "Hamilton", "Harker Heights", "Harlingen", "Hearne", "Henderson", "Hewitt", "Highland Park", "Highland Village", "Houston", "Humble", "Hunters Creek Village", "Huntsville", "Hurst", "Irving", "Jacksonville", "Jasper", "Jersey Village", "Katy", "Keller", "Kemah", "Kennedale", "Kerrville", "Killeen", "Kingsville", "Krum", "Kyle", "La Grange", "La Porte", "Lacy Lakeview", "Lago Vista", "Lake Jackson", "Lakeway", "Lamesa", "Lampasas", "Lancaster", "Laredo", "League City", "Leander", "Leon Valley", "Levelland", "Lewisville", "Littlefield", "Live Oak", "Lockhart", "Longview", "Lowry Crossing", "Lubbock", "Lucas", "Lufkin", "Mansfield", "Manvel", "Marble Falls", "Marshall", "McAllen", "McKinney", "Mesquite", "Midland", "Midlothian", "Mission", "Missouri City", "Mount Pleasant", "Muleshoe", "Murphy", "Nacogdoches", "Nassau Bay", "New Braunfels", "Newark", "Newton", "North Richland Hills", "Oak Ridge North", "Odessa", "Orange", "Overton", "Palacios", "Palestine", "Pampa", "Paris", "Pasadena", "Pearland", "Perryton", "Pflugerville", "Plainview", "Plano", "Port Aransas", "Port Arthur", "Port Lavaca", "Portland", "Richardson", "Richland Hills", "Ridge City", "Rio Grande City", "River Oaks", "Rockport", "Rockwall", "Rosenberg", "Round Rock", "Rowlett", "Royse City", "Rusk", "Sachse", "Saginaw", "San Angelo", "San Antonio", "San Benito", "San Marcos", "San Saba", "Santa Fe", "Schertz", "Seabrook", "Sealy", "Seguin", "Selma", "Seymour", "Shenandoah", "Sherman", "Smithville", "Snyder", "Socorro", "Sonora", "Southlake", "Southside Place", "Spring Valley", "Stafford", "Stephenville", "Sugar Land", "Sulphur Springs", "Sweeny", "Tahoka", "Taylor", "Temple", "Terrell", "Texarkana", "Texas City", "The Colony", "The Woodlands", "Tomball", "Tyler", "Universal City", "University Park", "Victoria", "Waco", "Watauga", "Waxahachie", "Weatherford", "Webster", "Weslaco", "West Columbia", "West Lake Hills", "West Orange", "West University Place", "Weston", "Wharton", "White Settlement", "Wichita Falls", "Willow Park", "Windcrest", "Woodway", "Wylie", "Yoakum"]

phil = SalesPerson.new
def personal_route(phil)
phil.schedule_city(Place.build("3900 16th st nw, DC", name: "Home"))
phil.schedule_city(Place.build("3505 Connecticut Ave, DC", name: "Petco"))
phil.schedule_city(Place.build("1250 24th st nw, DC", name: "Work"))
phil.schedule_city(Place.build("3100 14th St NW, DC", name: "Target"))
phil.schedule_city(Place.build("1440 P St NW, DC", name: "Whole Foods"))
phil.route("Work")
end

# puts "This is the route from home: #{phil.route}"

def load_cities(num_cities, sales_person)
times = 0
until times == num_cities do
@all_cities.shuffle.take(1).each do |city|
sales_person.schedule_city(Place.build("#{city}, TX"))
end
times +=1
sleep(2)
end
end

def Benchmark(num_cities)
phil = SalesPerson.new
load_cities(num_cities,phil)
Benchmark.bm do |x|
x.report do
phil.route
end
end
end

def pretty_time(seconds)
mm, ss = seconds.divmod(60) #=> [4515, 21]
hh, mm = mm.divmod(60) #=> [75, 15]
dd, hh = hh.divmod(24) #=> [3, 3]
"%d days, %d hours, %d minutes and %d seconds" % [dd, hh, mm, ss]
end

route = personal_route(phil)
puts route
puts "This is my personal route: #{route.map {|leg| leg.fetch(:point).name}}"
miles = phil.calculate_total_miles(route)
puts "Phil traveled #{miles} miles"
time = phil.calculate_traveling_time(miles, 25)*60*60
puts "It took phil #{pretty_time(time)}"
# puts "This is the benchmark for 2 cities:\n#{Benchmark(2)}"
# puts "This is the benchmark for 10 cities:\n#{Benchmark(10)}"
# puts "This is the benchmark for 50 cities:\n#{Benchmark(50)}"
# puts "This is the benchmark for 2 cities:\n#{Benchmark(200)}"

phil = SalesPerson.new
phil.schedule_city(Place.build("Dallas, TX"))
phil.schedule_city(Place.build("El Paso, TX"))
phil.schedule_city(Place.build("Austin, TX"))
phil.schedule_city(Place.build("Lubbock, TX"))

puts phil.route
19 changes: 13 additions & 6 deletions spec/calculates_route_spec.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
require_relative "spec_helper"
require_relative "../lib/calculates_route"
require_relative "../lib/place"

describe CalculatesRoute do
let(:dallas) {Place.build("Dallas, TX") }
let(:austin ) { Place.build("Austin, TX")}
let(:lubbock ) { Place.build("Lubbock, TX")}
let(:el_paso ) { Place.build("El Paso, TX")}
let(:dallas ) { Place.build("Dallas, TX") }
let(:austin ) { Place.build("Austin, TX") }
let(:lubbock ) { Place.build("Lubbock, TX") }
let(:el_paso ) { Place.build("El Paso, TX") }
let(:points ) { [dallas, el_paso, austin, lubbock] }

it "should calculate the route" do
points = [dallas, el_paso, austin, lubbock]
expected = [dallas, austin, lubbock, el_paso]
CalculatesRoute.calculate(points).should eq(expected)
route = CalculatesRoute.calculate(points)
route.map {|leg| leg.fetch(:point)}.should eq(expected)
end

it "should also output the distance of each leg" do
CalculatesRoute.calculate(points)[0].fetch(:distance).should eq(0)
end

end
9 changes: 5 additions & 4 deletions spec/map_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require_relative "spec_helper"
require_relative "../lib/map"
require 'geocoder'

Expand All @@ -10,17 +11,17 @@
end

it "should use the first item in the array" do
austin = stub("Austin")
dallas = stub("Dallas")
austin = double("Austin")
dallas = double("Dallas")
Geocoder.stub(:search) {[austin, dallas]}
Map.search("austin, tx").should eq(austin)
end
end

describe ":distance" do
it "should calculate distance between two sets of coordinates" do
alpha = stub
beta = stub
alpha = double
beta = double
Geocoder::Calculations.should_receive(:distance_between).with(alpha, beta)
Map.distance_between(alpha, beta)
end
Expand Down
33 changes: 24 additions & 9 deletions spec/place_spec.rb
Original file line number Diff line number Diff line change
@@ -1,34 +1,49 @@
require_relative "spec_helper"
require_relative "../lib/place"
require_relative "../lib/map"

describe Place do

it "should have a name" do
subject.should respond_to(:name)
it "should have a address" do
subject.should respond_to(:address)
end

it "should have a coordinates" do
subject.coordinates = [29,-95]
subject.coordinates.should eq([29,-95])
end

it "should have a pretty name" do
subject.should respond_to(:name)
end


describe ":build" do
let(:name) { "El Paso, TX"}
let(:result) { stub("el paso", coordinates: [29, -95])}
let(:address) { "El Paso, TX"}
let(:result) { double("el paso", coordinates: [29, -95])}
let(:name) {"El Paso"}

it "should return the pretty name if included" do
expect(Place.build(address, name: name).name).to eq("El Paso")
end

it "should return the address as name if pretty name isn't included" do
expect(Place.build(address).name).to eq("El Paso, TX")
end

it "should build from the map" do
Map.should_receive(:search).with(name).and_return(result)
Place.build(name)
Map.should_receive(:search).with(address).and_return(result)
Place.build(address)
end

it "should be place" do
Map.stub(:search).with(name).and_return(result)
Place.build(name).should be_a(Place)
Map.stub(:search).with(address).and_return(result)
Place.build(address).should be_a(Place)
end
end

describe "#to_s" do
it "should use the city as the to_s" do
it "should use the name as the to_s" do
subject.stub(:name) { "Boston" }
subject.to_s.should eq("Boston")
end
Expand Down
68 changes: 54 additions & 14 deletions spec/sales_person_spec.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,70 @@
require_relative "spec_helper"
require_relative "../lib/sales_person"
require_relative "../lib/calculates_route"
require_relative "../lib/place"

describe SalesPerson do

let(:city1) {Place.build("3505 Connecticut Ave, DC", name: "Petco")}
let(:city2) {Place.build("3900 16th st nw, DC", name: "Home")}
let(:city) {double}
let(:scheduled) { subject.schedule_city(city) }


it "should have many cities" do
city = stub
subject.schedule_city(city)
scheduled
subject.cities.should include(city)
end

it "should keep the cities only scheduled once" do
city = stub
expect{
subject.schedule_city(city)
subject.schedule_city(city)
scheduled
scheduled
}.to change(subject.cities,:count).by(1)
end

it "should calculate a route via the CalculatesRoute" do
cities = [stub, stub, stub]
subject.stub(:cities) { cities }
CalculatesRoute.should_receive(:calculate).with(cities)
subject.route
describe "#route" do
it "should calculate a route via the CalculatesRoute" do
cities = [Place, double, double]
subject.stub(:cities) { cities }
CalculatesRoute.should_receive(:calculate).with(cities)
subject.route
end

it "should returns the route from CalculatesRoute" do
route_stub = [double, double]
CalculatesRoute.stub(:calculate) { route_stub }
subject.route.should eq(route_stub)
end

it "should be able to add a starting city by name" do
subject.schedule_city(city1)
subject.schedule_city(city2)
starting_point = "home"
first_route_point = subject.route(starting_point)[0]
expect(first_route_point.fetch(:point)).to eq(city2)
end

it "should use first city if a starting city wasn't specified" do
subject.schedule_city(city1)
subject.schedule_city(city2)
first_route_point = subject.route[0]
expect(first_route_point.fetch(:point)).to eq(city1)
end
end

describe "#calculate_total_miles" do
it "should report the total miles traveled on the route" do
route = [{point: city1, distance: 0}, {point: city2, distance: 1.1}]
expect(subject.calculate_total_miles(route)).to eq(1.1)
end
end
it "should returns the route from CalculatesRoute" do
route_stub = [stub, stub]
CalculatesRoute.stub(:calculate) { route_stub }
subject.route.should eq(route_stub)

describe "#calculate_traveling_time" do
it "shoudld report the traveling time of the route" do
total_miles = 200
expect(subject.calculate_traveling_time(total_miles)).to eq(200/55)
end
end

end
Loading