Permalink
Browse files

Second talk - now with database connection!

  • Loading branch information...
barrettclark committed Nov 1, 2011
1 parent a222b4d commit 17e36fd5a7ca1624c32c4d96898e0fe2535d763d
Showing with 215 additions and 10 deletions.
  1. +3 −0 Gemfile
  2. +10 −0 Gemfile.lock
  3. +4 −0 README
  4. +13 −0 config/database.yml
  5. +49 −0 db/schema.sql
  6. +27 −0 lib/db_connection.rb
  7. +18 −6 lib/file_reader.rb
  8. +38 −2 lib/zipcode.rb
  9. +12 −0 test/db_connection_test.rb
  10. +1 −1 test/example1_test.rb
  11. +8 −0 test/test_helper.rb
  12. +32 −1 test/zipcode_test.rb
View
@@ -0,0 +1,3 @@
+source "http://rubygems.org"
+
+gem "pg", "~> 0.11.0"
View
@@ -0,0 +1,10 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ pg (0.11.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ pg (~> 0.11.0)
View
4 README
@@ -3,3 +3,7 @@ Dallas Ruby Plain Old Ruby (POR) Talk
Command-line app (script) that will read a zipcode file and do interesting stuff.
Week 1: Test/Unit
+
+Week 2: Database interaction
+
+Week 3: Logger, Inputs, and Rake (?)
View
@@ -0,0 +1,13 @@
+development:
+ dbname: dallas_por_zipcode
+ user: barrettclark
+ password:
+ host: localhost
+ port: 5432
+
+test:
+ dbname: dallas_por_zipcode_test
+ user: barrettclark
+ password:
+ host: localhost
+ port: 5432
View
@@ -0,0 +1,49 @@
+/*
+ Schema for a postgres setup
+*/
+
+-- Make the database
+-- NOTE: you will need to change the owner to one that exists in your DB
+CREATE DATABASE dallas_por_zipcode
+ WITH ENCODING='UTF8'
+ OWNER=barrettclark
+ CONNECTION LIMIT=-1;
+
+-- Make the zipcode table
+CREATE TABLE zipcode
+(
+ zip character(5) NOT NULL,
+ city character varying,
+ state character(2),
+ latitude real,
+ longitude real,
+ timezone integer,
+ dst integer,
+ CONSTRAINT zipcode_pk PRIMARY KEY (zip)
+)
+WITH (
+ OIDS = FALSE
+)
+;
+
+CREATE DATABASE dallas_por_zipcode_test
+ WITH ENCODING='UTF8'
+ OWNER=barrettclark
+ CONNECTION LIMIT=-1;
+
+-- Make the zipcode table
+CREATE TABLE zipcode
+(
+ zip character(5) NOT NULL,
+ city character varying,
+ state character(2),
+ latitude real,
+ longitude real,
+ timezone integer,
+ dst integer,
+ CONSTRAINT zipcode_pk PRIMARY KEY (zip)
+)
+WITH (
+ OIDS = FALSE
+)
+;
View
@@ -0,0 +1,27 @@
+class DBConnection
+ attr_reader :conn
+
+ def initialize
+ config = database_config
+ @conn = PGconn.open(
+ :dbname => config['dbname'],
+ :user => config['user'],
+ :port => config['port']
+ )
+ rescue
+ puts "Unable to connect to the #{ENV['SCRIPT_ENV']} database"
+ end
+
+ def exec(sql)
+ @conn.exec(sql)
+ end
+
+ private
+
+ def database_config
+ puts "Attempting to connect to the #{ENV['SCRIPT_ENV']} database"
+ filename = File.join(File.expand_path(File.dirname(__FILE__)), '..', 'config', 'database.yml')
+ config = YAML.load(File.read(filename))
+ config[ENV['SCRIPT_ENV'] || 'development']
+ end
+end
View
@@ -1,24 +1,36 @@
-require 'csv'
-require File.join(File.dirname(__FILE__), '../lib', 'zipcode')
require 'pp'
+require 'csv'
+require 'rubygems'
+require 'bundler/setup'
+Bundler.require
+$LOAD_PATH << File.join(File.dirname(__FILE__))
+require 'db_base'
+require 'zipcode'
+
+ENV['SCRIPT_ENV'] ||= 'development'
class FileReader
+
def self.read_zipcodes
- filename = File.join(File.dirname(__FILE__), 'zipcode.csv')
+ filename = File.join(File.dirname(__FILE__), '..', 'zipcode.csv')
options = {
:headers => true
}
CSV.foreach(filename, options) do |row|
- next if row.size != row.headers.size
+ next if row.size == 0
attributes = {}
row.headers.each { |field| attributes[field.to_sym] = row[field] }
- zipcode = ZipCode.new(attributes)
- # pp zipcode
+ zipcode = ZipCode.create(attributes)
end
end
end
# Run from the command line
if __FILE__ == $0
+ # Read and process the zip codes
FileReader.read_zipcodes
+
+ # Find a zip
+ zipcode = ZipCode.find('75019')
+ puts "#{zipcode.zip} is #{zipcode.city}, #{zipcode.state}"
end
View
@@ -1,5 +1,5 @@
-class ZipCode
- attr_accessor :zip, :city, :state, :latitude, :longitude, :timezone, :dst
+class ZipCode < DBBase
+ attr_accessor :id, :zip, :city, :state, :latitude, :longitude, :timezone, :dst
TIMEZONES = {
-5 => 'US/Eastern',
@@ -18,6 +18,42 @@ def initialize(attributes = {})
@dst = attributes[:dst].to_i
end
+ # We can insert records into the database easily
+ def self.create(attributes = {})
+ zipcode = ZipCode.new(attributes)
+ zipcode.create
+ zipcode
+ end
+
+ # We can also find records in the database and reconsititute them into
+ # ZipCode objects.
+ def self.find(zipcode)
+ sql = "SELECT * FROM zipcode WHERE zip = '#{zipcode}'"
+ result = connection.exec(sql)
+ attributes = Hash.new
+ result.fields.each_with_index do |field, idx|
+ attributes[field.to_sym] = result.column_values(idx).first
+ end
+ ZipCode.new(attributes)
+ end
+
+ def create
+ fields = [
+ # NOTE: The 'E' is to tell postgres that the string is (potentially) escaped
+ 'E' + PGconn.quote_connstr(@zip),
+ 'E' + PGconn.quote_connstr(@city),
+ 'E' + PGconn.quote_connstr(@state),
+ @latitude, @longitude, @timezone, @dst
+ ]
+ sql = "INSERT INTO zipcode VALUES (#{fields.join(',')})"
+ result = ZipCode.connection.exec(sql)
+ puts "#{sql}\n Tuples: #{result.cmd_tuples}" if ENV['SCRIPT_ENV'] == 'test'
+ # NOTE: insert queries do not return a result set
+ result
+ rescue PGError=>e
+ puts "#{e}: Unable to insert the zipcode: #{@zipcode}"
+ end
+
def timezone_name
TIMEZONES[@timezone]
end
View
@@ -0,0 +1,12 @@
+require 'test/unit'
+require File.join(File.expand_path(File.dirname(__FILE__)), 'test_helper')
+require 'db_connection'
+
+class DBConnectionTest < Test::Unit::TestCase
+ def setup
+ end
+
+ def test_the_connection
+ assert_equal PGconn::CONNECTION_OK, DBBase.connection.conn.status
+ end
+end
View
@@ -1,5 +1,5 @@
require 'test/unit'
-require 'test_helper'
+require File.join(File.expand_path(File.dirname(__FILE__)), 'test_helper')
require 'example1'
class Example1Test < Test::Unit::TestCase
View
@@ -1 +1,9 @@
+require 'pp'
+require 'rubygems'
+require 'bundler/setup'
+Bundler.require
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
+ENV['SCRIPT_ENV'] ||= 'test'
+
+require 'db_base'
View
@@ -1,10 +1,14 @@
require 'test/unit'
-require 'test_helper'
+require File.join(File.expand_path(File.dirname(__FILE__)), 'test_helper')
require 'zipcode'
class ZipCodeTest < Test::Unit::TestCase
# This runs before each test method
def setup
+ # nuke the database table
+ DBBase.connection.exec('TRUNCATE TABLE zipcode')
+
+ # create a fresh zipcode object
@zipcode = ZipCode.new(
:zip => '00210',
:city => 'Portsmouth',
@@ -46,4 +50,31 @@ def test_dst_question_mark
assert_nil zip.zip
assert_not_nil zip.latitude
end
+
+ def test_create
+ @zipcode.create
+ count = DBBase.connection.exec('SELECT COUNT(*) FROM zipcode')
+ assert_equal 1, count.getvalue(0,0).to_i
+ end
+
+ def test_create_class_method
+ zipcode = ZipCode.create(
+ :zip => '33558',
+ :city => "Land O' Lakes",
+ :state => 'FL',
+ :latitude => '28.157704',
+ :longitude => '-82.514615',
+ :timezone => '-5',
+ :dst => '1'
+ )
+ count = DBBase.connection.exec('SELECT COUNT(*) FROM zipcode')
+ assert_equal 1, count.getvalue(0,0).to_i
+ assert_equal '33558', zipcode.zip
+ end
+
+ def test_find_class_method
+ @zipcode.create
+ found_zipcode = ZipCode.find(@zipcode.zip)
+ assert_equal @zipcode.zip, found_zipcode.zip
+ end
end

0 comments on commit 17e36fd

Please sign in to comment.