auspost 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ # The list of files that should be ignored by Mr Bones.
2
+ # Lines that start with '#' are comments.
3
+ #
4
+ # A .gitignore file can be used instead by setting it as the ignore
5
+ # file in your Rakefile:
6
+ #
7
+ # Bones {
8
+ # ignore_file '.gitignore'
9
+ # }
10
+ #
11
+ # For a project with a C extension, the following would be a good set of
12
+ # exclude patterns (uncomment them if you want to use them):
13
+ # *.[oa]
14
+ # *~
15
+ announcement.txt
16
+ coverage
17
+ doc
18
+ pkg
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2009-12-26
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
@@ -0,0 +1,73 @@
1
+ auspost
2
+ by Cameron Barrie
3
+ http://wiki.github.com/whalec/auspost
4
+
5
+ == DESCRIPTION:
6
+
7
+ Give you the ability to search the Australia Post Web site with either Suburb or Postcode, and return an array of Locations.
8
+ Also add's validation methods to ActiveRecord Objects to allow you to validate that A Postcode + State + Suburb is correct
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ ActiveRecord Validations - validates_location
13
+
14
+
15
+ == SYNOPSIS:
16
+
17
+ class Foo < ActiveRecord::Base
18
+ include Auspost::Postie
19
+
20
+ before_save :check_address
21
+
22
+ def check_address
23
+ location?(:postcode => 2038, :suburb => "Annandale", :state => "NSW")
24
+ end
25
+ end
26
+
27
+ or
28
+
29
+ require 'auspost/active_record'
30
+ class Foo < ActiveRecord::Base
31
+
32
+ validate_location
33
+
34
+ end
35
+
36
+ == REQUIREMENTS:
37
+
38
+ Nokogiri - For teh XML parsing.
39
+
40
+ == INSTALL:
41
+
42
+ sudo gem install auspost
43
+
44
+ == TODO:
45
+
46
+ Validations for Active Record should take more options, including mapping the suburb, postcode and state to other columns
47
+
48
+ validate_location :suburb => :home, :state => "state.name", :on => [:create, :change], :if => lambda { !home.blank? && !postcode.blank? }
49
+
50
+ == LICENSE:
51
+
52
+ (The MIT License)
53
+
54
+ Copyright (c) 2009
55
+
56
+ Permission is hereby granted, free of charge, to any person obtaining
57
+ a copy of this software and associated documentation files (the
58
+ 'Software'), to deal in the Software without restriction, including
59
+ without limitation the rights to use, copy, modify, merge, publish,
60
+ distribute, sublicense, and/or sell copies of the Software, and to
61
+ permit persons to whom the Software is furnished to do so, subject to
62
+ the following conditions:
63
+
64
+ The above copyright notice and this permission notice shall be
65
+ included in all copies or substantial portions of the Software.
66
+
67
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
68
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
69
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
70
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
71
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
72
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
73
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ begin
2
+ require 'bones'
3
+ rescue LoadError
4
+ abort '### Please install the "bones" gem ###'
5
+ end
6
+
7
+ ensure_in_path 'lib'
8
+ require 'auspost'
9
+
10
+ task :default => 'test:run'
11
+ task 'gem:release' => 'test:run'
12
+
13
+ Bones {
14
+ name 'auspost'
15
+ authors 'Cameron Barrie'
16
+ email 'camwritescode@gmail.com'
17
+ dependencies ['nokogiri', "memcache-client"]
18
+ url 'http://www.camwritescode.com/auspost'
19
+ version Auspost::VERSION
20
+ }
21
+
22
+ # EOF
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib auspost]))
5
+
6
+ # Put your code here
7
+
@@ -0,0 +1,48 @@
1
+ module Auspost
2
+
3
+ # :stopdoc:
4
+ VERSION = '0.8.5'
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args )
20
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
21
+ end
22
+
23
+ # Returns the lpath for the module. If any arguments are given,
24
+ # they will be joined to the end of the path using
25
+ # <tt>File.join</tt>.
26
+ #
27
+ def self.path( *args )
28
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
29
+ end
30
+
31
+ # Utility method used to require all files ending in .rb that lie in the
32
+ # directory below this file that has the same name as the filename passed
33
+ # in. Optionally, a specific _directory_ name can be passed in such that
34
+ # the _filename_ does not have to be equivalent to the directory.
35
+ #
36
+ def self.require_all_libs_relative_to( fname, dir = nil )
37
+ dir ||= ::File.basename(fname, '.*')
38
+ search_me = ::File.expand_path(
39
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
40
+
41
+ Dir.glob(search_me).sort.each {|rb| require rb}
42
+ end
43
+
44
+ end # module Auspost
45
+ require 'open-uri'
46
+ require 'nokogiri'
47
+ require 'memcache'
48
+ require 'auspost/postie'
@@ -0,0 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), 'active_record/validations')
2
+ ActiveRecord::Base.class_eval do
3
+ include Auspost::Postie::ActiveRecord::Validations
4
+ include Auspost::Postie
5
+ end
@@ -0,0 +1,82 @@
1
+ module Auspost
2
+ module Postie
3
+
4
+ module ActiveRecord
5
+ module Validations
6
+
7
+ @@valid_attributes = {
8
+ :state => {:accessor => :state, :message => ["%s is not found in the %s postcode", :state, :postcode] },
9
+ :postcode => {:accessor => :postcode, :message => ["%s cannot be found", :postcode] },
10
+ :suburb => {:accessor => :suburb, :message => ["%s is not found in the %s postcode", :suburb, :postcode] },
11
+ }
12
+
13
+ def self.included(base)
14
+ base.extend ClassMethods
15
+ end
16
+
17
+
18
+ def validate_location
19
+ result = location?(map_attributes)
20
+ if result && !result.status
21
+ result.errors.each do |error|
22
+ message = @@valid_attributes[error.accessor][:message]
23
+ if message.is_a?(String)
24
+ errors.add(error.accessor, message)
25
+ else
26
+ mappings = message[1..-1].map do |x|
27
+ if x.to_s.include?(".")
28
+ methods = x.split(".")
29
+ send(methods.first).send(methods.last).to_s.upcase
30
+ else
31
+ send(x).to_s.upcase
32
+ end
33
+ end
34
+ errors.add(error.accessor, message.first % mappings)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def valid_attributes
41
+ @@valid_attributes
42
+ end
43
+
44
+ protected
45
+
46
+ def map_attributes
47
+ {
48
+ :state => get_column(:state),
49
+ :postcode => get_column(:postcode),
50
+ :suburb => get_column(:suburb)
51
+ }
52
+ end
53
+
54
+ def get_column(column)
55
+ if @@valid_attributes[column][:accessor].nil?
56
+ column
57
+ elsif @@valid_attributes[column][:accessor].is_a?(String) && @@valid_attributes[column][:accessor].include?(".")
58
+ association = @@valid_attributes[column][:accessor].split(".")
59
+ self.send(association.first.to_sym).send(association.last.to_sym)
60
+ else
61
+ self.send(@@valid_attributes[column][:accessor]) || column
62
+ end
63
+ end
64
+
65
+
66
+ module ClassMethods
67
+
68
+ include Validations
69
+
70
+ # I'll add some validations here...
71
+ def validate_location(*args)
72
+ valid_attributes.update(args.extract_options!)
73
+ validate :validate_location, valid_attributes
74
+ end
75
+
76
+
77
+ end
78
+ end
79
+ end #ActiveRecord
80
+
81
+ end
82
+ end
@@ -0,0 +1,172 @@
1
+ require 'cgi'
2
+ module Auspost
3
+
4
+ module Postie
5
+
6
+ class Cache
7
+
8
+ # Over ride these two class variables to change your memcache settings
9
+ @@memcache_host = "localhost"
10
+ @@memcache_port = "11211"
11
+
12
+ def initialize
13
+ if Object.const_defined? :Rails
14
+ Rails.cache
15
+ else
16
+ @cache = MemCache.new "#{@@memcache_host}:#{@@memcache_port}", :namespace => 'auspost'
17
+ end
18
+ end
19
+
20
+ def write(key, object)
21
+ if @cache
22
+ @cache.add(key, object) if @cache.servers.map{|s| s.alive?}.include?(true)
23
+ else
24
+ Rails.cache.write(key, object)
25
+ end
26
+ end
27
+
28
+ def read(key)
29
+ if @cache
30
+ @cache.get(key) if @cache.servers.map{|s| s.alive?}.include?(true)
31
+ else
32
+ Rails.cache.read(key)
33
+ end
34
+ end
35
+ end
36
+
37
+ class Suburb
38
+ def initialize(attrs)
39
+ @suburb = attrs[:suburb].downcase
40
+ @postcode = attrs[:postcode].to_i
41
+ @state = attrs[:state].downcase
42
+ end
43
+
44
+ def suburb?(attrs)
45
+ attrs[:suburb].downcase == @suburb
46
+ end
47
+
48
+ def state?(attrs)
49
+ attrs[:state].downcase == @state.downcase
50
+ end
51
+
52
+ def postcode?(attrs)
53
+ attrs[:postcode].to_i == @postcode
54
+ end
55
+
56
+ def eql?(attrs)
57
+ suburb?(attrs) && postcode?(attrs) && state?(attrs)
58
+ end
59
+
60
+ def to_i(attrs)
61
+ (suburb?(attrs) ? 1 : 0) +
62
+ (state?(attrs) ? 1 : 0) +
63
+ (postcode?(attrs) ? 1 : 0)
64
+ end
65
+ end
66
+
67
+ class Result
68
+
69
+ attr_reader :status, :errors
70
+
71
+ def initialize(status)
72
+ @status = status
73
+ @errors = [] if !@status
74
+ end
75
+
76
+ end
77
+
78
+ class Error
79
+
80
+ attr_reader :accessor
81
+
82
+ def initialize(accessor)
83
+ @accessor = accessor
84
+ end
85
+
86
+ end
87
+
88
+
89
+ # This is the method that returns whether a location is correct or not.
90
+ # It requires three attributes
91
+ # :postcode, :suburb & :state such as
92
+ # location?(:postcode => 2038, :suburb => "Annandale", :state => "NSW") #=> Result#status => true
93
+ # location?(:postcode => 2010, :suburb => "Annandale", :state => "NSW") #=> Result#status => false
94
+ # Result#errors => Error#accessor => :suburb && Error#message => "Annandale does not match 2010 postcode"
95
+ # The results of a request to a given postcode is cached in memcached if available or in
96
+ # your Rails.cache store if you're in a Rails project.
97
+ def location?(attrs = {})
98
+ map_attributes!(attrs)
99
+ url = "http://www1.auspost.com.au/postcodes/index.asp?Locality=&sub=1&State=&Postcode=#{CGI::escape(@postcode)}&submit1=Search"
100
+ @content = get(url)
101
+ check_results(attrs)
102
+ end
103
+
104
+
105
+ protected
106
+
107
+ def cache
108
+ @cache ||= set_cache
109
+ end
110
+
111
+ def set_cache
112
+ @cache = Cache.new
113
+ end
114
+
115
+ def get(url)
116
+ @results = check_cache || get_and_cache(url)
117
+ end
118
+
119
+ def check_cache
120
+ cache.read(@postcode)
121
+ end
122
+
123
+ def get_and_cache(url)
124
+ object = open(url)
125
+ @table = Nokogiri::HTML(object).xpath('//table[3]/tr/td[2]/table/tr/td/font/table/tr[2]/td/table/tr')
126
+ content = map_results
127
+ content.delete_if{|x| x.nil?}
128
+ cache.write(@postcode, content)
129
+ content
130
+ end
131
+
132
+ def map_attributes!(attrs)
133
+ raise ArgumentError, "You must supply a suburb" if attrs[:suburb].nil?
134
+ raise ArgumentError, "You must supply a state" if attrs[:state].nil?
135
+ raise ArgumentError, "You must supply a postcode" if attrs[:postcode].nil?
136
+ attrs.values.map!{|value| value.to_s.downcase! }
137
+ @postcode = attrs[:postcode].to_s
138
+ end
139
+
140
+ def map_results
141
+ @table.map do |row|
142
+ content = row.content.strip if row.content.include?("LOCATION:")
143
+ if content
144
+ s = content.index("LOCATION: ")
145
+ e = content.index(",")
146
+ suburb = content[s..e-1].split(" ").last
147
+ state = content[e+2..e+4]
148
+ final = Suburb.new(:suburb => suburb, :state => state, :postcode => @postcode)
149
+ end
150
+ end
151
+ end
152
+
153
+ def check_results(attrs)
154
+ if @results.map{|suburb| suburb.eql?(attrs)}.include?(true)
155
+ Result.new(true)
156
+ else
157
+ generate_result_with_errors(attrs)
158
+ end
159
+ end
160
+
161
+ def generate_result_with_errors(attrs)
162
+ result = Result.new(false)
163
+ error_count = @results.map{|r| r.to_i(attrs) }
164
+ main_error = @results[error_count.index(error_count.sort.last)]
165
+ result.errors << Error.new(:suburb) if !main_error.suburb?(attrs)
166
+ result.errors << Error.new(:state) if !main_error.state?(attrs)
167
+ result
168
+ end
169
+
170
+ end
171
+
172
+ end
@@ -0,0 +1,72 @@
1
+ require File.join(File.dirname(__FILE__), "../../spec_helper")
2
+ require File.join(File.dirname(__FILE__), "../../fixtures/postal_worker")
3
+
4
+ describe PostalWorker, "without auspost/active_record required" do
5
+
6
+ it "should not respond to a method validate_location" do
7
+ PostalWorker.should_not respond_to(:validate_location)
8
+ end
9
+
10
+ end
11
+
12
+
13
+ describe PostalWorker, "with auspost/active_record required" do
14
+
15
+ # require needs to be in the before block, otherwise it's called when the file is required or the describe blocks are called.
16
+ # causing failure to the above spec
17
+ before(:all) do
18
+ require 'auspost/active_record'
19
+ PostalWorker.validate_location
20
+ end
21
+
22
+ before do
23
+ @valid_attributes = {:address => "William St", :suburb => "Annandale", :postcode => "2038", :state => "NSW"}
24
+ @invalid_attributes = {:address => "William St", :suburb => "Annandale", :postcode => "2000", :state => "VIC"}
25
+ end
26
+
27
+ it "should have a method validate_location" do
28
+ PostalWorker.should respond_to(:validate_location)
29
+ end
30
+
31
+ it "should not be valid" do
32
+ @worker = PostalWorker.new(@invalid_attributes)
33
+ @worker.should_not be_valid
34
+ end
35
+
36
+ it "should validate an address" do
37
+ @worker = PostalWorker.new({:suburb => "Rozelle", :postcode => "2039", :state => "NSW"})
38
+ @worker.should be_valid
39
+ end
40
+
41
+ it "should return a useful error message on the state" do
42
+ @worker = PostalWorker.new(@invalid_attributes)
43
+ @worker.should_not be_valid
44
+ @worker.errors.on(:state).should eql("VIC is not found in the 2000 postcode")
45
+ @worker.errors.on(:suburb).should eql("ANNANDALE is not found in the 2000 postcode")
46
+ end
47
+
48
+ it "should return a useful error message on the suburb" do
49
+ @worker = PostalWorker.new(@valid_attributes.merge(:suburb => "Rozelle"))
50
+ @worker.should_not be_valid
51
+ @worker.errors.on(:suburb).should eql("ROZELLE is not found in the 2038 postcode")
52
+ @worker.errors.on(:state).should be_nil
53
+ end
54
+ end
55
+
56
+
57
+ describe MashedPostalWorker, "with options set on the validation" do
58
+ before(:all) do
59
+ require 'auspost/active_record'
60
+ MashedPostalWorker.validate_location :suburb => {:accessor => :city, :message => "That doesn't match up!"},
61
+ :postcode => {:accessor => :zipcode, :message => ["Postcode %s cannot be found", :zipcode]},
62
+ :state => {:accessor => "state.name", :message => ["%s is such a better state anyway *ducks*", "state.name"]}
63
+ end
64
+
65
+ it "should have a custom message on the suburb" do
66
+ @worker = MashedPostalWorker.new({:city => "Annandale", :zipcode => 3001, :state => State.new(:name => "NSW")})
67
+ @worker.should_not be_valid
68
+ @worker.errors.on(:suburb).should eql("That doesn't match up!")
69
+ @worker.errors.on(:state).should eql("NSW is such a better state anyway *ducks*")
70
+ end
71
+
72
+ end
@@ -0,0 +1,55 @@
1
+ require File.join(File.dirname(__FILE__), "../spec_helper")
2
+ include Auspost
3
+
4
+ describe Postie do
5
+ include Postie
6
+
7
+ it "should find Annandale and return true" do
8
+ location?({:postcode => "2038", :suburb => "Annandale", :state => "NSW"}).status.should eql(true)
9
+ end
10
+
11
+ it "shouldn't find that Surry Hills is in the 2000 postcode" do
12
+ location?({:postcode => "2000", :suburb => "Surry Hills", :state => "QLD"}).status.should_not eql(true)
13
+ end
14
+
15
+ # Testing this to ensure sub-strings don't pass as true
16
+ it "shoudn't find dale in 2038" do
17
+ location?({:postcode => "2038", :suburb => "dale", :state => "NSW"}).status.should_not eql(true)
18
+ end
19
+
20
+ it "shouldn't substring search" do
21
+ location?(:postcode => "2038", :suburb => "Annandale", :state => "N").status.should_not eql(true)
22
+ end
23
+
24
+ it "should raise if there's not a postcode" do
25
+ lambda { location?(:suburb => "Surry Hills", :state => "FOO") }.should raise_error(ArgumentError)
26
+ end
27
+
28
+ it "should raise if there's not a suburb" do
29
+ lambda { location?(:postcode => 2038, :state => "FOO") }.should raise_error(ArgumentError)
30
+ end
31
+
32
+ it "should raise if there's not a state" do
33
+ lambda { location?(:suburb => "Surry Hills", :postcode => 2038) }.should raise_error(ArgumentError)
34
+ end
35
+
36
+ it "should accept an Integer for the postcode" do
37
+ location?({:postcode => 2038, :suburb => "Annandale", :state => "NSW"}).status.should eql(true)
38
+ end
39
+
40
+ end
41
+
42
+ describe Postie, " with cached" do
43
+ include Postie
44
+
45
+ it "should cache a result" do
46
+ @io = mock(IO)
47
+ @string = mock(String)
48
+ @array = mock(Array)
49
+ Postie::Cache.should_receive(:new).and_return(@io)
50
+ @io.should_receive(:read).with("2038").and_return(@string)
51
+ @string.should_receive(:map).at_least(:once).and_return(@array)
52
+ @array.should_receive(:include?).with(true).and_return(true)
53
+ location?({:postcode => "2038", :suburb => "Annandale", :state => "NSW"}).status.should eql(true)
54
+ end
55
+ end
@@ -0,0 +1,187 @@
1
+
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="/include/stylesheet/miniapps.css" type="text/css">
5
+ <link rel="stylesheet" href="/include/stylesheet/mainstyle.css" type="text/css">
6
+ <title>Australia Post - Postcode Search</title>
7
+
8
+ <META NAME='ROBOTS' CONTENT='ALL'>
9
+ <META NAME='description' CONTENT='Search for Australian Postcode locations online'>
10
+ <META NAME='keywords' CONTENT='Australia Post Postcode Search Find locations'>
11
+
12
+ </head>
13
+ <script type="text/JavaScript">
14
+ <!--
15
+ function MM_preloadImages() { //v3.0
16
+ var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
17
+ var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
18
+ if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
19
+ }
20
+
21
+ function MM_swapImgRestore() { //v3.0
22
+ var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
23
+ }
24
+
25
+ function MM_findObj(n, d) { //v4.01
26
+ var p,i,x; if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
27
+ d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
28
+ if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
29
+ for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
30
+ if(!x && d.getElementById) x=d.getElementById(n); return x;
31
+ }
32
+
33
+ function MM_swapImage() { //v3.0
34
+ var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
35
+ if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
36
+ }
37
+ function cmdGo_Click() {
38
+ window.document.queryForm.submit(); }
39
+ //-->
40
+ </script>
41
+ <table width="780" border="0" cellspacing="0" cellpadding="0">
42
+ <tr class="top-header">
43
+ <td width="210"><a href="http://www.auspost.com.au"><img src="/auspost/images/display/logo.jpg" alt="Australia Post Home" width="182" height="45" border="0" /></a></td>
44
+ <td><img src="/auspost/images/display/b_arrow.gif" alt="Arrow" id="contact-arrow" width="13" height="14" />&nbsp;&nbsp;<a class="white-title" href="http://www.auspost.com.au/BCP/0,1080,CH3988%257EMO19,00.html">Jobs @ Post</a>&nbsp;&nbsp;&nbsp;<img src="/auspost/images/display/b_arrow.gif" alt="Arrow" id="contact-arrow" width="13" height="14" />&nbsp;&nbsp;&nbsp;<a class="white-title" href="http://www.auspost.com.au/CUP/0,,CHNONE%257EMO19,00.html">Contact Us</a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="search-title">Search Site :</span>&nbsp;<form id="queryForm" name="queryForm" action="http://search.auspost.com.au/cse/auspost/default.asp" method="get"><input id="queryBox" name="q" value="" size="20" />&nbsp;&nbsp;<a href="javascript:onClick=cmdGo_Click();" onmouseout="MM_swapImgRestore()" onmouseover="MM_swapImage('btngosearch','','/auspost/images/display/br_submit.gif',1)"><img name="btngosearch" id="btngosearch" src="/auspost/images/display/b_submit.gif" alt="submit" width="62" height="22" border="0" /></form></td>
45
+ </tr>
46
+ </table>
47
+ <table width="780" border="0" cellspacing="0" cellpadding="0">
48
+ <tr class="tabs-row">
49
+ <td width="19">&nbsp;</td>
50
+ <td width="101"><a href="http://www.auspost.com.au"><img src="/auspost/images/display/b_Home.gif" alt="Home" name="Image2" width="82" height="29" border="0" id="Image2" onmouseover="MM_swapImage('Image2','','/auspost/images/display/br_Home.gif',1)" onmouseout="MM_swapImgRestore()" /></a></td>
51
+ <td width="139"><a href="http://www.auspost.com.au/CAP/0,,CH2002%257EMO19,00.html"><img src="/auspost/images/display/b_GenServices.gif" alt="General Services" width="122" height="29" border="0" id="Image3" onmouseover="MM_swapImage('Image3','','/auspost/images/display/br_GenServices.gif',1)" onmouseout="MM_swapImgRestore()" /></a></td>
52
+ <td width="199"><a href="http://www.auspost.com.au/CAP/0,,CH2006%257EMO19,00.html"><img src="/auspost/images/display/b_Billing.gif" alt="Billing &amp; Financial" width="180" height="29" border="0" id="Image4" onmouseover="MM_swapImage('Image4','','/auspost/images/display/br_Billing.gif',1)" onmouseout="MM_swapImgRestore()" /></a></td>
53
+ <td width="154"><a href="http://www.auspost.com.au/CAP/0,,CH2005%257EMO19,00.html"><img src="/auspost/images/display/b_BusSolutions.gif" alt="Business Solutions" width="138" height="29" border="0" id="Image5" onmouseover="MM_swapImage('Image5','','/auspost/images/display/br_BusSolutions.gif',1)" onmouseout="MM_swapImgRestore()" /></a></td>
54
+ <td><a href="http://www.auspost.com.au/CAP/0,,CH2007%257EMO19,00.html"><img src="/auspost/images/display/b_AboutUs.gif" alt="About Us" width="85" height="29" border="0" id="Image6" onmouseover="MM_swapImage('Image6','','/auspost/images/display/br_AboutUs.gif',1)" onmouseout="MM_swapImgRestore()" /></a></td>
55
+ <td width="50"><div align="right"><img src="/auspost/images/display/cnr_btm_r.gif" alt="corner" width="39" height="29" /></div></td>
56
+ </tr>
57
+ <tr>
58
+ <td colspan="7"><img src="/auspost/images/display/transparent 3x3.gif" alt="Blank" width="8" height="8" /></td>
59
+ </tr>
60
+ </table>
61
+ <table border="0" cellpadding="0" cellspacing="0" width="780">
62
+ <tr>
63
+ <td valign="top" id="td-site-app">
64
+
65
+ <ul id="site-apps" style="padding-right: 5px;">
66
+ <li><img src="/auspost/images/display/h_Useful_Tools.gif" alt="Useful Tools" width="113" height="29" /></li>
67
+ <li><img src="/auspost/images/display/i_FindPostcode.gif" alt="Find a postcode" width="22" height="22" />&nbsp;&nbsp;<a class="min-apps" href="http://www1.auspost.com.au/postcodes/">Find a postcode</a></li>
68
+ <li><img src="/auspost/images/display/i_CalcPostageRates.gif" alt="Calculate postage rates" width="22" height="22" />&nbsp;&nbsp;<a class="min-apps" href="http://www1.auspost.com.au/pac/">Calculate postage rates</a></li>
69
+ <li><img src="/auspost/images/display/i_ExpressPostPlatTrackg.gif" alt="Australia post delivery tracking" width="22" height="22" />&nbsp;&nbsp;<a class="min-apps" href="http://www.auspost.com.au/track">Track my item</a></li>
70
+ <li><img src="/auspost/images/display/i_FindPostalOutlet.gif" alt="Find a postal outlet" width="22" height="22" />&nbsp;&nbsp;<a class="min-apps" href="http://www.auspost.com.au/pol/">Find a postal outlet</a></li>
71
+ <li><img src="/auspost/images/display/i_FindPostalBox.gif" alt="Find a street posting box" width="22" height="22" />&nbsp;&nbsp;<a class="min-apps" href="http://www.auspost.com.au/pol/">Find a street posting box</a></li>
72
+ <li><img src="/auspost/images/display/i_PayBillsOnline.gif" alt="Pay bills online" width="22" height="22" />&nbsp;&nbsp;<a class="min-apps" href="http://www.postbillpay.com.au/">Pay bills online</a></li>
73
+ <li><img src="/auspost/images/display/i_IntnlPostageDetails.gif" alt="International Postage Details" width="22" height="22" />&nbsp;&nbsp;<a class="min-apps" href="http://www1.auspost.com.au/international/">International postage details</a></li>
74
+ <li><img src="/auspost/images/display/i_PostGuides.gif" alt="Post guides" width="22" height="22" />&nbsp;&nbsp;<a class="min-apps" href="http://www.auspost.com.au/BCP/0,,CH3851%257EMO19,00.html">Post guides</a></li>
75
+ </ul>
76
+
77
+ </td>
78
+ <td valign="top">
79
+ <table width="600" border="0" cellpadding="0" cellspacing="10">
80
+ <tr valign="top">
81
+ <td width="493" align="center" valign="top"><font face="verdana,Helv,Arial" size="2">
82
+
83
+
84
+ <table border="0" cellpadding="0" cellspacing="0" width="490">
85
+ <tr>
86
+ <!-- banner header -->
87
+ <td colspan="2" bgcolor="#B9E3DE"><img src="../images_new/postcode_search/funct_postcodesearch.gif" alt="Postcode Search" width="327" height="32"></td>
88
+ </tr>
89
+ <tr>
90
+ <!-- Results row -->
91
+ <td width="330" valign="top"><table width="330" border="0" cellpadding="5" cellspacing="0">
92
+
93
+ <tr>
94
+ <td valign="top" width="7" bgcolor="#ff0000"><font face="verdana, arial, helv" size="2" color="#ffffff"><strong>1.</strong></td>
95
+ <td><font face="arial, helv" size="2"> <strong>LOCATION:</strong> ANNANDALE, NSW<br />
96
+
97
+ <strong>POSTCODE:</strong> 2038<br />
98
+
99
+ <strong>CATEGORY:</strong> Delivery Area<br />
100
+
101
+ </td>
102
+ </tr>
103
+ <tr>
104
+ <td colspan="2"><hr noshade size="1">
105
+ </td>
106
+ </tr>
107
+
108
+ </table></td>
109
+ <td width="150" valign="top" bgcolor="#B9E3DE"><table width="150" border="0" cellpadding="5" cellspacing="0">
110
+ <!-- Results summary table -->
111
+ <tr>
112
+ <!-- Show results for each state -->
113
+ <td width="150" style="background-color: #B9E3DE; font-size:100%"><a href="index.asp">Search Again</a>
114
+ <hr noshade size="1">
115
+ <strong>Returned Matches:</strong> 1
116
+ <hr noshade size="1">
117
+
118
+ <strong>Breakdown</strong>
119
+ <br />New South Wales: 1
120
+ </td>
121
+ </tr>
122
+
123
+ </table></td>
124
+ </tr>
125
+ </table>
126
+
127
+ </td>
128
+ </tr>
129
+ </table>
130
+ </td>
131
+ </tr>
132
+ </table>
133
+
134
+ <table width="780" border="0" cellspacing="0" cellpadding="0">
135
+ <!---------- SPACER ------------------------------------------------------------------->
136
+ <tr>
137
+ <td valign="top" height="20">
138
+ </td>
139
+ </tr>
140
+ <!---------- end SPACER --------------------------------------------------------------->
141
+
142
+ <!---------- FIRST LEVEL NAVIGATION ------------------------------------------------------>
143
+ <tr>
144
+ <td id="td-foot">
145
+ <br />
146
+ <ul id="footer-list">
147
+ <li><img src="/auspost/images/display/a_right.gif" alt="Arrow" width="4" height="7" /> <a class="footer" href="http://www.auspost.com.au/APC/CDA/Sitemap/APC_CDA_Sitemap/0,1084,CHNONE%257EMO19,00.html">Site map</a>&nbsp;&nbsp;&nbsp;&nbsp;</li>
148
+ <li><img src="/auspost/images/display/a_right.gif" alt="Arrow" width="4" height="7" /> <a class="footer" href="http://www.auspost.com.au/CUP/0,1050,CHNONE%257EMO19,00.html">Contact Us</a>&nbsp;&nbsp;&nbsp;&nbsp;</li>
149
+ <li><img src="/auspost/images/display/a_right.gif" alt="Arrow" width="4" height="7" /> <a class="footer" href="http://www.auspost.com.au/APC/CDA/Site_Governance/APC_CDA_Privacy_Policy/">Privacy</a>&nbsp;&nbsp;&nbsp;&nbsp;</li>
150
+ <li><img src="/auspost/images/display/a_right.gif" alt="Arrow" width="4" height="7" /> <a class="footer" href="http://www.auspost.com.au/APC/CDA/Site_Governance/APC_CDA_Terms_Conditions/">Terms &amp; Conditions </a></li>
151
+ </ul>
152
+
153
+
154
+ <!-- Track File Downloads with Google -->
155
+ <script language="javascript" type="text/javascript" src="/APC/CDA/CSS/gatag.js" ></script>
156
+
157
+ <!--Google tracking -->
158
+ <script type="text/javascript">
159
+ var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
160
+ document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
161
+ </script>
162
+
163
+ <script type="text/javascript">
164
+ <!-- Auspost roll up account -->
165
+ try {
166
+ var APTracker = _gat._getTracker("UA-2225711-5");
167
+ APTracker._setDomainName(".auspost.com.au");
168
+ APTracker._trackPageview("WWW1" + document.location.pathname);
169
+ } catch(err) {}
170
+
171
+ <!-- Auspost account -->
172
+ try {
173
+ var pageTracker = _gat._getTracker("UA-2225711-1");
174
+ pageTracker._setDomainName(".auspost.com.au");
175
+ pageTracker._trackPageview();
176
+ } catch(err) {}
177
+
178
+ <!-- WWW1 account -->
179
+ try {
180
+ var pageTracker = _gat._getTracker("UA-2225711-3");
181
+ pageTracker._setDomainName(".auspost.com.au");
182
+ pageTracker._trackPageview();
183
+ } catch(err) {}
184
+ </script>
185
+
186
+ </body>
187
+ </html>
@@ -0,0 +1,39 @@
1
+ gem 'sqlite3-ruby'
2
+ require 'sqlite3'
3
+ require 'active_record'
4
+
5
+ ActiveRecord::Base.establish_connection(
6
+ :adapter => "sqlite3",
7
+ :database => ":memory:"
8
+ )
9
+ ActiveRecord::Base.connection.create_table(:postal_workers) do |t|
10
+ t.string :address
11
+ t.string :postcode
12
+ t.string :suburb
13
+ t.string :state
14
+ end
15
+
16
+
17
+ ActiveRecord::Base.connection.create_table(:mashed_postal_workers) do |t|
18
+ t.string :addr
19
+ t.string :zipcode
20
+ t.string :city
21
+ t.integer :state_id
22
+ end
23
+
24
+ ActiveRecord::Base.connection.create_table(:states) do |t|
25
+ t.string :name
26
+ end
27
+
28
+
29
+ class PostalWorker < ActiveRecord::Base;end
30
+
31
+ class MashedPostalWorker < ActiveRecord::Base
32
+
33
+ belongs_to :state
34
+
35
+ end
36
+
37
+ class State < ActiveRecord::Base
38
+ has_many :mashed_postal_workers
39
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib auspost]))
4
+
5
+ Spec::Runner.configure do |config|
6
+ # == Mock Framework
7
+ #
8
+ # RSpec uses it's own mocking framework by default. If you prefer to
9
+ # use mocha, flexmock or RR, uncomment the appropriate line:
10
+ #
11
+ # config.mock_with :mocha
12
+ # config.mock_with :flexmock
13
+ # config.mock_with :rr
14
+ end
15
+
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: auspost
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.5
5
+ platform: ruby
6
+ authors:
7
+ - Cameron Barrie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-02 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bones
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.2.0
24
+ version:
25
+ description: |-
26
+ Give you the ability to search the Australia Post Web site with either Suburb or Postcode, and return an array of Locations.
27
+ Also add's validation methods to ActiveRecord Objects to allow you to validate that A Postcode + State + Suburb is correct
28
+ email: camwritescode@gmail.com
29
+ executables:
30
+ - auspost
31
+ extensions: []
32
+
33
+ extra_rdoc_files:
34
+ - History.txt
35
+ - README.txt
36
+ - bin/auspost
37
+ files:
38
+ - .bnsignore
39
+ - History.txt
40
+ - README.txt
41
+ - Rakefile
42
+ - bin/auspost
43
+ - lib/auspost.rb
44
+ - lib/auspost/active_record.rb
45
+ - lib/auspost/active_record/validations.rb
46
+ - lib/auspost/postie.rb
47
+ - spec/auspost/active_record/validations_spec.rb
48
+ - spec/auspost/postie_spec.rb
49
+ - spec/fixtures/auspost.html
50
+ - spec/fixtures/postal_worker.rb
51
+ - spec/spec_helper.rb
52
+ has_rdoc: true
53
+ homepage: http://www.camwritescode.com/auspost
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --main
59
+ - README.txt
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project: auspost
77
+ rubygems_version: 1.3.5
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Give you the ability to search the Australia Post Web site with either Suburb or Postcode, and return an array of Locations
81
+ test_files:
82
+ - spec/auspost/active_record/validations_spec.rb
83
+ - spec/auspost/postie_spec.rb