Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Episode 5

  • Loading branch information...
commit 26194f62ae48a206c8a096ddcaff2a6373f67d0f 1 parent 8488ed1
Rob Conery authored
View
1  .autotest
@@ -0,0 +1 @@
+require 'autotest/growl'
View
12 config.ru
@@ -1,6 +1,18 @@
require "rubygems"
require "music_catalog/app"
+require 'music_catalog/model/artist'
+require 'music_catalog/model/album'
+require 'music_catalog/model/genre'
+require 'music_catalog/model/track'
+
+require "customer/app"
+require "customer/model/customer"
+require "customer/model/cart_item"
map "/" do
run MusicCatalog
end
+
+map "/customer" do
+ run CustomerApp
+end
View
23 customer/app.rb
@@ -0,0 +1,23 @@
+require "rubygems"
+require "sinatra/base"
+require "mongo_mapper"
+
+class CustomerApp < Sinatra::Base
+
+ post "/add_item" do
+ id = params[:album_id]
+ album = Album.find(id)
+
+ unless(album.nil?)
+ login = @env['REMOTE_ADDR']
+ customer = Customer.find_or_create(login)
+ customer.add_item_to_cart(:sku => album.original_id,
+ :item_price => album.price,
+ :name => album.name,
+ :quantity => 1)
+ customer.save
+ "Thanks! Added"
+ end
+ end
+
+end
View
13 customer/model/cart_item.rb
@@ -0,0 +1,13 @@
+class CartItem
+ include MongoMapper::Document
+
+ key :sku, String
+ key :name, String
+ key :item_price, Float
+ key :quantity, Integer
+ key :add_at, Time
+
+ timestamps!
+
+ belongs_to :customer
+end
View
68 customer/model/customer.rb
@@ -0,0 +1,68 @@
+class Customer
+ include MongoMapper::Document
+
+ key :original_id, Integer
+ key :login, String
+ key :first_name, String
+ key :last_name, String
+ key :company, String
+ key :email, String
+ key :address, String
+ key :city, String
+ key :state, String
+ key :country, String
+ key :phone, String
+ key :fax, String
+ key :postal_code, String
+
+ many :cart_items
+
+ timestamps!
+
+ def self.find_or_create(login)
+ Customer.find_by_login(login) or Customer.new(:login => login)
+ end
+ def add_item_to_cart(hash)
+ found = self.cart_items.find_by_sku(hash[:sku])
+ unless(found)
+ ci = CartItem.create(hash)
+ self.cart_items << ci
+ self.save
+ else
+ found.quantity += hash[:quantity].to_i
+ found.save
+ end
+
+ end
+ def empty_cart
+ self.cart_items.delete_all
+
+ end
+ def cart_total
+ total = 0.0
+ self.cart_items.each{|i| total += i.item_price}
+ total
+
+ end
+ def cart_contains(sku)
+ self.cart_items.find_by_sku(sku)
+ end
+ def remove_item_from_cart(sku)
+ found = cart_contains(sku)
+ if(found)
+ self.cart_items.delete(found.id)
+ end
+
+ end
+ def self.migrate_cart(from_login, to_login)
+ from = Customer.find_by_login(from_login)
+ to = Customer.find_by_login(to_login)
+ if(from && to)
+ to.empty_cart
+ from.cart_items.each {|i| to.cart_items << i}
+ from.empty_cart
+ from.save
+ to.save
+ end
+ end
+end
View
BIN  db/chinook.xml
Binary file not shown
View
20 music_catalog/app.rb
@@ -1,8 +1,28 @@
require "rubygems"
require "sinatra/base"
+require "mongo_mapper"
class MusicCatalog < Sinatra::Base
+
+ configure :development do
+ MongoMapper.connection = Mongo::Connection.new("localhost")
+ MongoMapper.database = "SinatraStore"
+ end
+ before do
+ @genres = Genre.all
+ end
get "/" do
haml :index
end
+
+ get "/:genre" do
+ @genre = Genre.find_by_slug(params[:genre])
+ haml :genre
+ end
+
+ get "/:artist/:album" do
+ @artist = Artist.find_by_slug(params[:artist])
+ @album = Album.find_by_artist_id_and_slug(@artist.id, params[:album])
+ haml :album
+ end
end
View
17 music_catalog/model/album.rb
@@ -0,0 +1,17 @@
+require "mongo_mapper"
+
+class Album
+ include MongoMapper::Document
+
+ key :original_id, Integer
+ key :name, String
+ key :price, Float
+ key :slug, String
+
+ belongs_to :artist
+ belongs_to :genre
+ many :tracks
+ def thumb
+ "/images/placeholder.gif"
+ end
+end
View
12 music_catalog/model/artist.rb
@@ -0,0 +1,12 @@
+require "mongo_mapper"
+
+class Artist
+
+ include MongoMapper::Document
+
+ key :original_id, Integer
+ key :name, String
+ key :slug, String
+ many :albums
+
+end
View
11 music_catalog/model/genre.rb
@@ -0,0 +1,11 @@
+require "mongo_mapper"
+
+class Genre
+
+ include MongoMapper::Document
+
+ key :original_id, Integer
+ key :name, String
+ key :slug, String
+ many :albums
+end
View
16 music_catalog/model/track.rb
@@ -0,0 +1,16 @@
+require "mongo_mapper"
+
+class Track
+ include MongoMapper::Document
+
+ key :original_id, Integer
+ key :name, String
+ key :composer, String
+ key :milliseconds, Integer
+ key :bytes, Integer
+ key :unit_price, Float
+
+ belongs_to :album
+ belongs_to :genre
+
+end
View
150 rakefile.rb
@@ -0,0 +1,150 @@
+require 'rubygems'
+require 'mongo_mapper'
+require 'nokogiri'
+require 'music_catalog/model/artist'
+require 'music_catalog/model/album'
+require 'music_catalog/model/genre'
+require 'music_catalog/model/track'
+require 'customer/model/customer'
+
+def open_xml
+ f = File.open("db/chinook.xml")
+ @doc = Nokogiri::XML(f)
+end
+def init_mongo
+ MongoMapper.connection = Mongo::Connection.new("localhost")
+ MongoMapper.database = "SinatraStore"
+end
+def init_model
+ Artist.delete_all
+ Track.delete_all
+ Genre.delete_all
+ Album.delete_all
+end
+def parse_key(key, &block)
+ items = @doc.css(key)
+ items.each do |item|
+ def item.method_missing(meth, *args)
+ found = self.at_css(meth.to_s)
+ raise "Can't find #{meth}" if found.nil?
+ found.content
+ end
+ yield(item)
+ end
+end
+
+def sluggify(title)
+ title.downcase.gsub(" ","-").gsub("&","-n-").gsub("/","-").gsub(",","")
+end
+
+def load_genres
+ parse_key("Genre") do |item|
+ thing = Genre.create!(:name => item.Name,
+ :original_id => item.GenreId.to_i,
+ :slug => sluggify(item.Name))
+
+ puts "#{thing.name} created..."
+ end
+end
+def load_customers
+ parse_key("Customer") do |item|
+ thing = Customer.create!(:original_id => item.CustomerId.to_i,
+ :first_name => item.FirstName,
+ :last_name => item.LastName,
+ :company => item.Company,
+ :email =>item.Email,
+ :address => item.Address,
+ :city => item.City,
+ :state => item.State,
+ :country => item.Country,
+ :phone => item.Phone,
+ :fax => item.Fax,
+ :postal_code => item.PostalCode)
+
+ puts "#{thing.first_name} #{thing.last_name} created..."
+ end
+
+end
+def load_artists
+ parse_key("Artist") do |item|
+ thing = Artist.create!(:name => item.Name,
+ :original_id => item.ArtistId.to_i,
+ :slug => sluggify(item.Name))
+ puts " - #{thing.name}"
+ end
+end
+
+def load_albums
+ parse_key("Album") do |item|
+ thing = Album.create!(:name => item.Title,
+ :original_id => item.AlbumId.to_i,
+ :slug => sluggify(item.Title))
+ puts " - #{thing.name}"
+ end
+end
+def load_tracks
+ parse_key("Track") do |item|
+ thing = Track.create!(:name => item.Name,
+ :composer => item.Composer,
+ :milliseconds => item.Milliseconds.to_i,
+ :bytes => item.Bytes.to_i,
+ :unit_price => item.UnitPrice.to_f,
+ :original_id => item.TrackId.to_i)
+ puts " - #{thing.name}"
+ end
+end
+def associate
+ parse_key("Track") do |item|
+ track = Track.find_by_original_id(item.TrackId.to_i)
+ album = Album.find_by_original_id(item.AlbumId.to_i)
+ genre = Genre.find_by_original_id(item.GenreId.to_i)
+ puts "... adding #{track.name} to #{album.name} and setting to #{genre.name}"
+ album.tracks << track
+ album.genre = genre
+ album.price = 8.99
+ album.save
+ end
+ parse_key("Album") do |item|
+
+ album = Album.find_by_original_id(item.AlbumId.to_i)
+ artist = Artist.find_by_original_id(item.ArtistId.to_i)
+ puts "... setting #{album.name} artist to #{artist.name}"
+ artist.albums << album
+ artist.save
+ end
+
+
+end
+desc "import music catalog"
+task :import_catalog do
+
+ puts "Opening xml file.."
+ open_xml
+
+ puts "Connecting to MongoDb..."
+ init_mongo
+
+ puts "Deleting existing.."
+ init_model
+
+ load_genres
+ load_albums
+ load_artists
+ load_tracks
+ associate
+end
+desc "import customers"
+task :import_customers do
+
+ puts "Opening xml file.."
+ open_xml
+
+ puts "Connecting to MongoDb..."
+ init_mongo
+
+ puts "Deleting existing.."
+ Customer.delete_all
+
+ load_customers
+end
+
View
107 spec/models/customer_spec.rb
@@ -0,0 +1,107 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+require "customer/model/customer"
+require "customer/model/cart_item"
+
+describe "Customer Retrieval" do
+
+ before :all do
+ Customer.delete_all
+ CartItem.delete_all
+ end
+
+ it 'should create a customer when email not found' do
+ customer = Customer.find_or_create('test')
+ customer.should_not be nil
+ end
+
+ it 'should return existing user' do
+ Customer.create!(:login => "existing")
+ customer = Customer.find_or_create('existing')
+ customer.should_not be nil
+ end
+
+end
+
+describe "Shopping Cart" do
+ it 'should add new sku to cart items' do
+ c = Customer.find_or_create("test")
+ c.add_item_to_cart(:sku => "test",
+ :name => "test item",
+ :quantity => 1,
+ :item_price => 12.00)
+ c.cart_items.count.should == 1
+ end
+
+ it 'should total up the cart items' do
+
+ c = Customer.find_or_create("test")
+ c.add_item_to_cart(:sku => "test",
+ :name => "test item",
+ :quantity => 1,
+ :item_price => 12.00)
+ c.cart_total.should == 12.0
+ end
+
+
+ it 'should increment quantity when adding same sku' do
+
+ c = Customer.find_or_create("test")
+ 2.times do
+ c.add_item_to_cart(:sku => "test",
+ :name => "test item",
+ :quantity => 1,
+ :item_price => 12.00)
+ c.cart_total.should == 12.0
+ end
+ c.cart_items.count.should == 1
+ end
+
+ it 'should clear items' do
+ c = Customer.find_or_create("test")
+ c.add_item_to_cart(:sku => "test",
+ :name => "test item",
+ :quantity => 1,
+ :item_price => 12.00)
+ c.empty_cart
+ c.cart_items.count.should == 0
+ end
+
+ it 'should let me know if cart item exists' do
+ c = Customer.find_or_create("test")
+ c.add_item_to_cart(:sku => "test",
+ :name => "test item",
+ :quantity => 1,
+ :item_price => 12.00)
+ c.cart_contains("test").should_not be nil
+ end
+
+ it 'should remove items' do
+
+ c = Customer.find_or_create("test")
+ c.add_item_to_cart(:sku => "test",
+ :name => "test item",
+ :quantity => 1,
+ :item_price => 12.00)
+ c.remove_item_from_cart("test")
+ c.cart_contains("test").should be nil
+ end
+
+ it 'should migrate from one user to another - replacing items' do
+ c1 = Customer.find_or_create("test5")
+ c1.add_item_to_cart(:sku => "sku",
+ :item_price => 12.0,
+ :quantity => 1,
+ :name => "Test Product")
+ c2 = Customer.find_or_create("test6")
+ c2.add_item_to_cart(:sku => "sku",
+ :item_price => 12.0,
+ :quantity => 1,
+ :name => "Test Product")
+
+ Customer.migrate_cart("test5","test6")
+ c1.cart_items.count.should be 0
+ c2.cart_items.count.should be 1
+
+ end
+
+end
View
4 spec/spec.opts
@@ -0,0 +1,4 @@
+--colour
+--format progress
+--loadby mtime
+
View
18 spec/spec_helper.rb
@@ -0,0 +1,18 @@
+require 'rubygems'
+require 'sinatra'
+require 'rack/test'
+require 'spec'
+require 'spec/autorun'
+require 'spec/interop/test'
+require 'mongo_mapper'
+require 'music_catalog/app'
+
+# set test environment
+set :environment, :test
+set :run, false
+set :raise_errors, true
+set :logging, false
+
+MongoMapper.connection = Mongo::Connection.new('localhost')
+MongoMapper.database = "SinatraStoreTest"
+
View
17 views/album.haml
@@ -0,0 +1,17 @@
+#album-details
+ %h3=@album.name
+ %img{:src => "#{@album.thumb}", :alt => "#{@album.name}"}
+ %p
+ %em Genre
+ = @album.genre.name
+ %p
+ %em Artist
+ = @artist.name
+ %p
+ %em Price
+ = @album.price
+ %p
+ %form{:action => "/customer/add_item", :method => "post"}
+ %input{:type => "hidden", :name => "album_id", :value => @album.id}
+ %input{:type => "submit", :value => 'Add to cart'}
+
View
10 views/genre.haml
@@ -0,0 +1,10 @@
+.genre
+ %p
+ %strong=@genre.name
+ %ul{:id => "album-list"}
+ - @genre.albums.each do |a|
+ %li
+ %a{:href => "#{a.artist.slug}/#{a.slug}", :title => "#{a.name}"}
+ %img{:src => "#{a.thumb}", :alt => "#{a.name}"}
+ %span=a.name
+
View
27 views/layout.haml
@@ -3,12 +3,12 @@
%head
%link{:href => "/css/site.css", :rel => "stylesheet", :type => "text/css"}/
%title
- ASP.NET MVC Music Store
+ Sinatra Music Store
%body
#container
#header
%h1
- %a{:href => "/"} ASP.NET MVC MUSIC STORE
+ %a{:href => "/"} SINATRA MUSIC STORE
%ul#navlist
%li.first
%a#current{:href => "/"} Home
@@ -21,26 +21,9 @@
#content-section
#categories
%ul
- %li
- %a{:href => "/Store/Browse/Rock"} Rock
- %li
- %a{:href => "/Store/Browse/Jazz"} Jazz
- %li
- %a{:href => "/Store/Browse/Metal"} Metal
- %li
- %a{:href => "/Store/Browse/Alternative"} Alternative
- %li
- %a{:href => "/Store/Browse/Disco"} Disco
- %li
- %a{:href => "/Store/Browse/Blues"} Blues
- %li
- %a{:href => "/Store/Browse/Latin"} Latin
- %li
- %a{:href => "/Store/Browse/Reggae"} Reggae
- %li
- %a{:href => "/Store/Browse/Pop"} Pop
- %li
- %a{:href => "/Store/Browse/Classical"} Classical
+ - @genres.each do |g|
+ %li
+ %a{:href => "/#{g.slug}"}=g.name
#main
= yield
#footer
Please sign in to comment.
Something went wrong with that request. Please try again.