zester 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,27 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ .rvmrc
20
+
21
+ ## PROJECT::SPECIFIC
22
+ Gemfile.lock
23
+ *.gem
24
+ .bundle
25
+ pkg/*
26
+ spec/zillow_api_key.yml
27
+ spec/vcr_cassettes/*.yml
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in zester.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem "nokogiri"
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Tom Cocca (http://github.com/tcocca)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # Zester
2
+
3
+ Zester is an API wrapper written in Ruby designed to be used with multiple api keys for a system that has multiple users each with their own key.
4
+
5
+ Zillow API documentation can be found here: http://www.zillow.com/howto/api/APIOverview.htm
6
+
7
+ You must have a Zillow API Key (ZWSID) which you can obtain here: https://www.zillow.com/webservice/Registration.htm
8
+
9
+ ## Usage
10
+
11
+ Every api call can be made off of a Zester::Client so the first step is to instantiate a client:
12
+
13
+ ```ruby
14
+ zester = Zester::Client.new('your_api_key')
15
+ ```
16
+
17
+ You do not need to pass the 'zws-id' param to any api calls, this param will be added automatically by the client and the api key that was passed in.
18
+
19
+ All other params for each api call are passed as a hash with the keys as strings in the exact same format as specified in the Zillow API docs
20
+
21
+ ### Home Valuation
22
+
23
+ Zillow 'Home Valuation' calls are accessed off of the client#valuation method:
24
+
25
+ ```ruby
26
+ zester.valuation.zestimate('zpid' => 12345)
27
+ zester.valuation.search_results('address' => '123 Main Street', 'citystatezip' => 'Boston, MA')
28
+ zester.valuation.chart('zpid' => 12345)
29
+ zester.valuation.comps('zpid' => 12345)
30
+ ```
31
+
32
+ The #chart call defaults to the 'unit-type' param to 'dollar', this can be overridden in the params hash.
33
+ The #comps call defaults to the 'count' param to 10, this can be overridden in the params hash.
34
+
35
+ ### Property Details
36
+
37
+ Zillow 'Property Details' api calls are accessed off the client#property method:
38
+
39
+ ```ruby
40
+ zester.property.deep_comps('zpid' => 12345)
41
+ zester.property.deep_search_results('address' => '123 Main Street', 'citystatezip' => 'Boston, MA')
42
+ zester.property.updated_property_details('zpid' => 12345)
43
+ ```
44
+
45
+ The #deep_comps call defaults to the 'count' param to 10, this can be overridden in the params hash.
46
+
47
+ ### Neighborhood Data
48
+
49
+ Zillow 'Neighborhood Data' api calls are accessed off the client#neighborhood method:
50
+
51
+ ```ruby
52
+ zester.neighborhood.demographics('state' => 'MA', 'city' => 'Boston')
53
+ zester.neighborhood.region_children('regionID' => 123, 'state' => 'MA')
54
+ zester.neighborhood.region_chart('state' => 'MA')
55
+ ```
56
+
57
+ The #region_chart call defaults to the 'unit-type' param to 'dollar', this can be overridden in the params hash.
58
+
59
+ ### Mortgage Rates
60
+
61
+ Zillow 'Mortgage Rates' api calls are accessed off the client#mortgage method:
62
+
63
+ ```ruby
64
+ zester.mortgage.rate_summary('MA')
65
+ ```
66
+
67
+ The #rate_summary method does not take a params hash, instead it only takes an optional state param as a string. This is the only param that applies.
68
+
69
+ #### Responses
70
+
71
+ Each API call method returns a Zester::Response instance. Zester::Response converts the XML response into a Hashie::Rash (http://github.com/tcocca/rash) object which basically takes the hash returned by MultiXml and converts that into an object so that you can use dot notation to access the keys/values as methods. Rash converts the keys from camelCase into underscore_case to make it more ruby-esque.
72
+
73
+ Zester::Response also provides a nice method missing to make getting at the values from the xml easier. The method missing works off of the <response> node of the xml. So in the following example:
74
+
75
+ ```xml
76
+ <?xml version="1.0" encoding="utf-8" ?>
77
+ <Chart:chart xmlns:Chart="http://www.zillow.com/vstatic/3/static/xsd/Chart.xsd">
78
+ <request>
79
+ <zpid>48749425</zpid>
80
+ <unit-type>percent</unit-type>
81
+ <width>300</width>
82
+ <height>150</height>
83
+ </request>
84
+ <message>
85
+ <text>Request successfully processed</text>
86
+ <code>0</code>
87
+ </message>
88
+ <response>
89
+ <url>http://www.zillow.com/app?chartDuration=1year&chartType=partner&height=150&page=webservice%2FGetChart&service=chart&showPercent=true&width=300&zpid=48749425</url>
90
+ </response>
91
+ </Chart:chart>
92
+ <!-- H:11 T:1ms S:311 -->
93
+ ```
94
+
95
+ Which is response from a call to zester.valuation.chart (http://www.zillow.com/howto/api/GetChart.htm) you have the following:
96
+
97
+ ```ruby
98
+ response = zester.valuation.chart('zpid' => '48749425')
99
+ response.success? #=> true
100
+ response.response_code #=> 0
101
+ response.url #=> http://www.zillow.com/app?chartDuration=1year&chartType=partner&height=150&page=webservice%2FGetChart&service=chart&showPercent=true&width=300&zpid=48749425
102
+ ```
103
+
104
+ So, anything inside the <response> node can be accessed directly by the method missing.
105
+
106
+ You can still get at anything in the xml by accessing the key off of the body:
107
+
108
+ ```ruby
109
+ response.body.message
110
+ response.body.message.text
111
+ response.body.request
112
+ response.body.response
113
+ response.body.response.url
114
+ ```
115
+
116
+ Zester::Response also provides a fea extra helper methods:
117
+
118
+ ```ruby
119
+ response.response_code #=> returns response.body.message.code.to_i
120
+ response.success? #=> returns true if response_code == 0
121
+ response.error_message #=> returns response.body.message.text
122
+ response.near_limit? #=> returns true of response.body.message.limit_warning == 'true'
123
+ ```
124
+
125
+ ## TODO
126
+
127
+ * Support the Mortgage Calculator api calls
128
+ * Create YARD documentation
129
+
130
+ ## Contributing to Zester
131
+
132
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
133
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
134
+ * Fork the project.
135
+ * Start a feature/bugfix branch.
136
+ * Commit and push until you are happy with your contribution.
137
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
138
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
139
+
140
+ ## Copyright
141
+
142
+ Copyright (c) 2012 Tom Cocca (http://github.com/tcocca). See LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :test => :spec
8
+ task :default => :spec
@@ -0,0 +1,36 @@
1
+ module Zester
2
+ class Client
3
+
4
+ include HTTParty
5
+ format :xml
6
+ base_uri "http://www.zillow.com/webservice"
7
+
8
+ attr_accessor :zws_id
9
+
10
+ def initialize(zws_id)
11
+ self.zws_id = zws_id
12
+ self.class.default_params "zws-id" => zws_id
13
+ end
14
+
15
+ def perform_get(endpoint, params = {})
16
+ self.class.get("/#{endpoint}.htm", :query => params)
17
+ end
18
+
19
+ def property
20
+ @property = Zester::Property.new(self)
21
+ end
22
+
23
+ def mortgage
24
+ @mortgage = Zester::Mortgage.new(self)
25
+ end
26
+
27
+ def valuation
28
+ @valuation = Zester::Valuation.new(self)
29
+ end
30
+
31
+ def neighborhood
32
+ @neighborhood = Zester::Neighborhood.new(self)
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ module Zester
2
+ class Mortgage < Resource
3
+
4
+ def rate_summary(state = nil)
5
+ get_results('GetRateSummary', :rate_summary, {'state' => state})
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ module Zester
2
+ class Neighborhood < Resource
3
+
4
+ def demographics(params = {})
5
+ if params['regionid'].nil? &&
6
+ (params['state'].nil? || params['city'].nil?) &&
7
+ (params['city'].nil? || params['neighborhood'].nil?) &&
8
+ params['zip'].nil?
9
+ raise ArgumentError, "At least rid or state & city or city & neighborhood or zip is required"
10
+ end
11
+ get_results('GetDemographics', :demographics, params)
12
+ end
13
+
14
+ def region_children(params = {})
15
+ if params['regionId'].nil? && params['state'].nil?
16
+ raise ArgumentError, "At least region_id or state is required"
17
+ end
18
+ get_results('GetRegionChildren', :regionchildren, params)
19
+ end
20
+
21
+ def region_chart(params = {})
22
+ params['unit-type'] ||= 'dollar'
23
+ get_results('GetRegionChart', :regionchart, params)
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Zester
2
+ class Property < Resource
3
+
4
+ def deep_comps(params = {})
5
+ if params['zpid'].nil?
6
+ raise ArgumentError, "zpid is required"
7
+ end
8
+ params['count'] ||= 10
9
+ get_results('GetDeepComps', :comps, params)
10
+ end
11
+
12
+ def deep_search_results(params = {})
13
+ if params['address'].nil? || params['citystatezip'].nil?
14
+ raise ArgumentError, "address and citystatezip are required"
15
+ end
16
+ get_results('GetDeepSearchResults', :searchresults, params)
17
+ end
18
+
19
+ def updated_property_details(params = {})
20
+ if params['zpid'].nil?
21
+ raise ArgumentError, "zpid is required"
22
+ end
23
+ get_results('GetUpdatedPropertyDetails', :updated_property_details, params)
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module Zester
2
+ class Resource
3
+
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ self.client = client
8
+ end
9
+
10
+ def get_results(endpoint, resource_type, params = {})
11
+ Response.new(self.client.perform_get(endpoint, params), resource_type)
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,47 @@
1
+ module Zester
2
+ class Response
3
+
4
+ attr_accessor :body
5
+
6
+ def initialize(response_body, response_type)
7
+ self.body = Hashie::Rash.new(response_body).send(response_type)
8
+ end
9
+
10
+ def success?
11
+ !self.response_code.nil? && self.response_code == 0
12
+ end
13
+
14
+ def error_message
15
+ self.message.text if self.message && self.message.text
16
+ end
17
+
18
+ def response_code
19
+ self.message.code.to_i if self.message && self.message.code
20
+ end
21
+
22
+ def near_limit?
23
+ self.message && self.message.limit_warning && self.message.limit_warning == "true"
24
+ end
25
+
26
+ def message
27
+ self.body.message if self.body.respond_to?(:message)
28
+ end
29
+
30
+ def method_missing(method_name, *args)
31
+ if self.body.respond_to?(:response) && self.body.response.respond_to?(method_name)
32
+ self.body.response.send(method_name)
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def respond_to?(method_name)
39
+ if self.body.respond_to?(:response) && self.body.response.respond_to?(method_name)
40
+ true
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ module Zester
2
+ class Valuation < Resource
3
+
4
+ def zestimate(params = {})
5
+ if params['zpid'].nil?
6
+ raise ArgumentError, "zpid is required"
7
+ end
8
+ get_results('GetZestimate', :zestimate, params)
9
+ end
10
+
11
+ def search_results(params = {})
12
+ if params['address'].nil? || params['citystatezip'].nil?
13
+ raise ArgumentError, "address and citystatezip are required"
14
+ end
15
+ get_results('GetSearchResults', :searchresults, params)
16
+ end
17
+
18
+ def chart(params = {})
19
+ if params['zpid'].nil?
20
+ raise ArgumentError, "zpid is required"
21
+ end
22
+ params['unit-type'] ||= 'dollar'
23
+ get_results('GetChart', :chart, params)
24
+ end
25
+
26
+ def comps(params = {})
27
+ if params['zpid'].nil?
28
+ raise ArgumentError, "zpid is required"
29
+ end
30
+ params['count'] ||= 10
31
+ get_results('GetComps', :comps, params)
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Zester
2
+ VERSION = "0.1.0"
3
+ end
data/lib/zester.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'httparty'
2
+ require 'rash'
3
+ require 'zester/client'
4
+ require 'zester/resource'
5
+ require 'zester/response'
6
+ require 'zester/mortgage'
7
+ require 'zester/neighborhood'
8
+ require 'zester/property'
9
+ require 'zester/valuation'
@@ -0,0 +1,20 @@
1
+ require 'rspec'
2
+ require 'zester'
3
+ require 'webmock/rspec'
4
+ require 'vcr'
5
+
6
+ MultiXml.parser = :nokogiri
7
+
8
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ config.treat_symbols_as_metadata_keys_with_true_values = true
12
+ config.run_all_when_everything_filtered = true
13
+ config.filter_run :focus
14
+ end
15
+
16
+ VCR.configure do |c|
17
+ c.cassette_library_dir = 'spec/vcr_cassettes'
18
+ c.hook_into :webmock
19
+ c.default_cassette_options = { :record => :new_episodes }
20
+ end
@@ -0,0 +1,5 @@
1
+ ZWS_ID = YAML.load_file(File.join(File.dirname(__FILE__), '/../zillow_api_key.yml'))["zws_id"]
2
+
3
+ def new_zester
4
+ Zester::Client.new(ZWS_ID)
5
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zester::Client do
4
+ let!(:zester) {new_zester}
5
+
6
+ it "should set the zws_id" do
7
+ zester.zws_id.should == ZWS_ID
8
+ zester.class.default_params.should == {'zws-id' => ZWS_ID}
9
+ end
10
+
11
+ it "should set the base uri for the client" do
12
+ zester.class.base_uri.should == "http://www.zillow.com/webservice"
13
+ end
14
+
15
+ it "should return an instance of Zester::Valuation for valuation" do
16
+ zester.valuation.should be_kind_of(Zester::Valuation)
17
+ zester.valuation.client.should == zester
18
+ end
19
+
20
+ it "should return an instance of Zester::Valuatiion for property" do
21
+ zester.property.should be_kind_of(Zester::Property)
22
+ zester.property.client.should == zester
23
+ end
24
+
25
+ it "should return an instance of Zester::Valuatiion for mortgage" do
26
+ zester.mortgage.should be_kind_of(Zester::Mortgage)
27
+ zester.mortgage.client.should == zester
28
+ end
29
+
30
+ it "should return an instance of Zester::Valuatiion for neighborhood" do
31
+ zester.neighborhood.should be_kind_of(Zester::Neighborhood)
32
+ zester.neighborhood.client.should == zester
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zester::Mortgage do
4
+ let!(:zester) {new_zester}
5
+ let!(:mortgage) {zester.mortgage}
6
+
7
+ context "rate_summary" do
8
+ it "should be a success" do
9
+ VCR.use_cassette('mortgage') do
10
+ response = mortgage.rate_summary
11
+ response.success?.should be_true
12
+ response.today.should_not be_nil
13
+ response.last_week.should_not be_nil
14
+ end
15
+ end
16
+
17
+ it "should be a failure" do
18
+ VCR.use_cassette('mortgage') do
19
+ response = mortgage.rate_summary('XZ')
20
+ response.success?.should be_false
21
+ response.response_code.should == 501
22
+ response.error_message.should_not be_nil
23
+ response.should_not respond_to(:today)
24
+ response.should_not respond_to(:last_week)
25
+ end
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zester::Neighborhood do
4
+ let!(:zester) {new_zester}
5
+ let!(:neighborhood) {zester.neighborhood}
6
+
7
+ context "demographics" do
8
+ it "should check for required params" do
9
+ VCR.use_cassette('neighborhood') do
10
+ expect {neighborhood.demographics}.to raise_error(ArgumentError, "At least rid or state & city or city & neighborhood or zip is required")
11
+ expect {neighborhood.demographics('regionid' => 'MA')}.to_not raise_error(ArgumentError)
12
+ expect {neighborhood.demographics('state' => 'MA')}.to raise_error(ArgumentError, "At least rid or state & city or city & neighborhood or zip is required")
13
+ expect {neighborhood.demographics('city' => 'Boston')}.to raise_error(ArgumentError, "At least rid or state & city or city & neighborhood or zip is required")
14
+ expect {neighborhood.demographics('neighborhood' => 'Back Bay')}.to raise_error(ArgumentError, "At least rid or state & city or city & neighborhood or zip is required")
15
+ expect {neighborhood.demographics('zip' => '02118')}.to_not raise_error(ArgumentError)
16
+ expect {neighborhood.demographics('state' => 'MA', 'city' => 'Boston')}.to_not raise_error(ArgumentError)
17
+ expect {neighborhood.demographics('city' => 'Boston', 'neighborhood' => 'Back Bay')}.to_not raise_error(ArgumentError)
18
+ end
19
+ end
20
+
21
+ it "should return a success" do
22
+ VCR.use_cassette('neighborhood') do
23
+ response = neighborhood.demographics('state' => 'MA', 'city' => 'Boston')
24
+ response.success?.should be_true
25
+ response.region.should_not be_nil
26
+ response.pages.should_not be_nil
27
+ response.market.should_not be_nil
28
+ response.links.should_not be_nil
29
+ response.charts.should_not be_nil
30
+ end
31
+ end
32
+
33
+ it "should return a failure" do
34
+ VCR.use_cassette('neighborhood') do
35
+ response = neighborhood.demographics('regionid' => 'MA')
36
+ response.success?.should be_false
37
+ response.response_code.should == 500
38
+ response.error_message.should_not be_nil
39
+ response.should_not respond_to(:region)
40
+ response.should_not respond_to(:pages)
41
+ response.should_not respond_to(:market)
42
+ response.should_not respond_to(:links)
43
+ response.should_not respond_to(:charts)
44
+ end
45
+ end
46
+ end
47
+
48
+ context "region_children" do
49
+ it "should check for required params" do
50
+ VCR.use_cassette('neighborhood') do
51
+ expect {neighborhood.region_children}.to raise_error(ArgumentError, "At least region_id or state is required")
52
+ expect {neighborhood.region_children('state' => 'MA')}.to_not raise_error(ArgumentError)
53
+ expect {neighborhood.region_children('regionId' => 'MA')}.to_not raise_error(ArgumentError)
54
+ end
55
+ end
56
+
57
+ it "should return a success" do
58
+ VCR.use_cassette('neighborhood') do
59
+ response = neighborhood.region_children('state' => 'MA')
60
+ response.success?.should be_true
61
+ response.region.should_not be_nil
62
+ response.subregiontype.should_not be_nil
63
+ response.list.should_not be_nil
64
+ end
65
+ end
66
+
67
+ it "should return a failure" do
68
+ VCR.use_cassette('neighborhood') do
69
+ response = neighborhood.region_children('state' => 'YZ')
70
+ response.success?.should be_false
71
+ response.response_code.should == 502
72
+ response.error_message.should_not be_nil
73
+ response.should_not respond_to(:region)
74
+ response.should_not respond_to(:subregiontype)
75
+ response.should_not respond_to(:list)
76
+ end
77
+ end
78
+ end
79
+
80
+ context "region_chart" do
81
+ it "should not require any params" do
82
+ VCR.use_cassette('neighborhood') do
83
+ expect {neighborhood.region_chart}.to_not raise_error(ArgumentError)
84
+ expect {neighborhood.region_chart('unit-type' => 'dollar')}.to_not raise_error(ArgumentError)
85
+ end
86
+ end
87
+
88
+ it "should return a success" do
89
+ VCR.use_cassette('neighborhood') do
90
+ response = neighborhood.region_chart('unit-type' => 'dollar')
91
+ response.success?.should be_true
92
+ response.zindex.should_not be_nil
93
+ response.url.should_not be_nil
94
+ response.links.should_not be_nil
95
+ end
96
+ end
97
+
98
+ it "should return a failure" do
99
+ VCR.use_cassette('neighborhood') do
100
+ response = neighborhood.region_chart('unit-type' => 'none')
101
+ response.success?.should_not be_true
102
+ response.response_code.should == 502
103
+ response.error_message.should_not be_nil
104
+ response.should_not respond_to(:zindex)
105
+ response.should_not respond_to(:url)
106
+ response.should_not respond_to(:links)
107
+ end
108
+ end
109
+ end
110
+
111
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zester::Property do
4
+ let!(:zester) {new_zester}
5
+ let!(:property) {zester.property}
6
+
7
+ context "deep_comps" do
8
+ it "should check for required params" do
9
+ VCR.use_cassette('property') do
10
+ expect {property.deep_comps}.to raise_error(ArgumentError, "zpid is required")
11
+ expect {property.deep_comps('zpid' => '12345')}.to_not raise_error(ArgumentError)
12
+ end
13
+ end
14
+
15
+ it "should return a success" do
16
+ VCR.use_cassette('property') do
17
+ response = property.deep_comps('zpid' => '48749425')
18
+ response.success?.should be_true
19
+ response.properties.should_not be_nil
20
+ end
21
+ end
22
+
23
+ it "should return a failure" do
24
+ VCR.use_cassette('property') do
25
+ response = property.deep_comps('zpid' => 'xyz')
26
+ response.success?.should be_false
27
+ response.response_code.should == 500
28
+ response.error_message.should_not be_nil
29
+ response.should_not respond_to(:properties)
30
+ end
31
+ end
32
+ end
33
+
34
+ context "deep_search_results" do
35
+ it "should check for required params" do
36
+ VCR.use_cassette('property') do
37
+ expect {property.deep_search_results}.to raise_error(ArgumentError, "address and citystatezip are required")
38
+ expect {property.deep_search_results('address' => '2114 Bigelow Ave', 'citystatezip' => 'Seattle, WA')}.to_not raise_error(ArgumentError)
39
+ end
40
+ end
41
+
42
+ it "should return a success" do
43
+ VCR.use_cassette('property') do
44
+ response = property.deep_search_results('address' => '2114 Bigelow Ave', 'citystatezip' => 'Seattle, WA')
45
+ response.success?.should be_true
46
+ response.results.should_not be_nil
47
+ end
48
+ end
49
+
50
+ it "should return a failure" do
51
+ VCR.use_cassette('property') do
52
+ response = property.deep_search_results('address' => '2114 Bigelow Ave', 'citystatezip' => '123')
53
+ response.success?.should be_false
54
+ response.response_code.should == 508
55
+ response.error_message.should_not be_nil
56
+ response.should_not respond_to(:results)
57
+ end
58
+ end
59
+ end
60
+
61
+ context "updated_property_details" do
62
+ it "should check for required params" do
63
+ VCR.use_cassette('property') do
64
+ expect {property.updated_property_details}.to raise_error(ArgumentError, "zpid is required")
65
+ expect {property.updated_property_details('zpid' => '12345')}.to_not raise_error(ArgumentError)
66
+ end
67
+ end
68
+
69
+ it "should return a success" do
70
+ VCR.use_cassette('property') do
71
+ response = property.updated_property_details('zpid' => '48749425')
72
+ response.success?.should be_true
73
+ response.zpid.should_not be_nil
74
+ response.page_view_count.should_not be_nil
75
+ response.address.should_not be_nil
76
+ response.links.should_not be_nil
77
+ response.edited_facts.should_not be_nil
78
+ response.neighborhood.should_not be_nil
79
+ response.school_district.should_not be_nil
80
+ response.elementary_school.should_not be_nil
81
+ response.middle_school.should_not be_nil
82
+ end
83
+ end
84
+
85
+ it "should return a failure" do
86
+ VCR.use_cassette('property') do
87
+ response = property.updated_property_details('zpid' => 'xyz')
88
+ response.success?.should be_false
89
+ response.response_code.should == 500
90
+ response.error_message.should_not be_nil
91
+ response.should_not respond_to(:address)
92
+ response.should_not respond_to(:links)
93
+ response.should_not respond_to(:edited_facts)
94
+ response.should_not respond_to(:neighborhood)
95
+ response.should_not respond_to(:school_district)
96
+ response.should_not respond_to(:elementary_school)
97
+ response.should_not respond_to(:middle_school)
98
+ end
99
+ end
100
+ end
101
+
102
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zester::Resource do
4
+ let!(:zester) {new_zester}
5
+ let!(:resource) {Zester::Resource.new(zester)}
6
+
7
+ it "should instantiate with a client" do
8
+ resource.client.should_not be_nil
9
+ end
10
+
11
+ context "get_results" do
12
+ it "should return a response" do
13
+ VCR.use_cassette('resource') do
14
+ response = resource.get_results('GetRateSummary', :rate_summary)
15
+ response.should be_instance_of(Zester::Response)
16
+ end
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zester::Response do
4
+ let!(:zester) {new_zester}
5
+ let!(:resource) {Zester::Resource.new(zester)}
6
+
7
+ it "should return a success" do
8
+ VCR.use_cassette('response') do
9
+ response = resource.get_results('GetRateSummary', :rate_summary)
10
+ response.success?.should be_true
11
+ response.message.should_not be_nill
12
+ response.message.code.should == "0"
13
+ response.response_code.should == 0
14
+ response.should be_instance_of(Zester::Response)
15
+ response.body.should be_instance_of(Hashie::Rash)
16
+ end
17
+ end
18
+
19
+ it "should return a failure" do
20
+ VCR.use_cassette('response') do
21
+ response = resource.get_results('GetRateSummary', :rate_summary, {'state' => 'XZ'})
22
+ response.success?.should be_false
23
+ response.error_message.should_not be_nil
24
+ response.message.code.should == "501"
25
+ response.response_code.should == 501
26
+ end
27
+ end
28
+
29
+ context "respond_to?" do
30
+ it "should respond to methods called from body.response" do
31
+ VCR.use_cassette('response') do
32
+ response = resource.get_results('GetRateSummary', :rate_summary)
33
+ response.should respond_to(:today)
34
+ response.should respond_to(:last_week)
35
+ end
36
+ end
37
+
38
+ it "should not respond to methods called off body.response" do
39
+ VCR.use_cassette('response') do
40
+ response = resource.get_results('GetRateSummary', :rate_summary, {'state' => 'XZ'})
41
+ response.should_not respond_to(:today)
42
+ response.should_not respond_to(:last_week)
43
+ end
44
+ end
45
+ end
46
+
47
+ context "method_missing" do
48
+ it "should call methods off of the body.response" do
49
+ VCR.use_cassette('response') do
50
+ response = resource.get_results('GetRateSummary', :rate_summary)
51
+ response.today.should == response.body.response.today
52
+ end
53
+ end
54
+
55
+ it "should raise errors when the method does not exist" do
56
+ VCR.use_cassette('response') do
57
+ response = resource.get_results('GetRateSummary', :rate_summary, {'state' => 'XZ'})
58
+ expect {response.today}.to raise_error(NoMethodError)
59
+ expect {response.last_week}.to raise_error(NoMethodError)
60
+ end
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zester::Valuation do
4
+ let!(:zester) {new_zester}
5
+ let!(:valuation) {zester.valuation}
6
+
7
+ context "zestimate" do
8
+ it "should check for required params" do
9
+ VCR.use_cassette('valuation') do
10
+ expect {valuation.zestimate}.to raise_error(ArgumentError, "zpid is required")
11
+ expect {valuation.zestimate('zpid' => '12345')}.to_not raise_error(ArgumentError)
12
+ end
13
+ end
14
+
15
+ it "should return a success" do
16
+ VCR.use_cassette('valuation') do
17
+ response = valuation.zestimate('zpid' => '48749425')
18
+ response.success?.should be_true
19
+ response.zpid.should_not be_nil
20
+ response.links.should_not be_nil
21
+ response.address.should_not be_nil
22
+ response.zestimate.should_not be_nil
23
+ response.local_real_estate.should_not be_nil
24
+ response.regions.should_not be_nil
25
+ end
26
+ end
27
+
28
+ it "should return a failure" do
29
+ VCR.use_cassette('valuation') do
30
+ response = valuation.zestimate('zpid' => 'xyz')
31
+ response.success?.should be_false
32
+ response.response_code.should == 500
33
+ response.error_message.should_not be_nil
34
+ response.should_not respond_to(:zpid)
35
+ response.should_not respond_to(:links)
36
+ response.should_not respond_to(:address)
37
+ response.should_not respond_to(:zestimate)
38
+ response.should_not respond_to(:local_real_estate)
39
+ response.should_not respond_to(:regions)
40
+ end
41
+ end
42
+ end
43
+
44
+ context "search_results" do
45
+ it "should check for required params" do
46
+ VCR.use_cassette('valuation') do
47
+ expect {valuation.search_results}.to raise_error(ArgumentError, "address and citystatezip are required")
48
+ expect {valuation.search_results('address' => '2114 Bigelow Ave', 'citystatezip' => 'Seattle, WA')}.to_not raise_error(ArgumentError)
49
+ end
50
+ end
51
+
52
+ it "should return a success" do
53
+ VCR.use_cassette('valuation') do
54
+ response = valuation.search_results('address' => '2114 Bigelow Ave', 'citystatezip' => 'Seattle, WA')
55
+ response.success?.should be_true
56
+ response.results.should_not be_nil
57
+ end
58
+ end
59
+
60
+ it "should return a failure" do
61
+ VCR.use_cassette('valuation') do
62
+ response = valuation.search_results('address' => '2114 Bigelow Ave', 'citystatezip' => '123')
63
+ response.success?.should be_false
64
+ response.response_code.should == 508
65
+ response.error_message.should_not be_nil
66
+ response.should_not respond_to(:results)
67
+ end
68
+ end
69
+ end
70
+
71
+ context "chart" do
72
+ it "should check for required params" do
73
+ VCR.use_cassette('valuation') do
74
+ expect {valuation.chart}.to raise_error(ArgumentError, "zpid is required")
75
+ expect {valuation.chart('zpid' => '12345')}.to_not raise_error(ArgumentError)
76
+ end
77
+ end
78
+
79
+ it "should return a success" do
80
+ VCR.use_cassette('valuation') do
81
+ response = valuation.chart('zpid' => '48749425')
82
+ response.success?.should be_true
83
+ response.url.should_not be_nil
84
+ response.graphsanddata.should_not be_nil
85
+ end
86
+ end
87
+
88
+ it "should return a failure" do
89
+ VCR.use_cassette('valuation') do
90
+ response = valuation.chart('zpid' => 'xyz')
91
+ response.success?.should be_false
92
+ response.response_code.should == 500
93
+ response.error_message.should_not be_nil
94
+ response.should_not respond_to(:url)
95
+ response.should_not respond_to(:graphsanddata)
96
+ end
97
+ end
98
+ end
99
+
100
+ context "comps" do
101
+ it "should check for required params" do
102
+ VCR.use_cassette('valuation') do
103
+ expect {valuation.comps}.to raise_error(ArgumentError, "zpid is required")
104
+ expect {valuation.comps('zpid' => '12345')}.to_not raise_error(ArgumentError)
105
+ end
106
+ end
107
+
108
+ it "should return a success" do
109
+ VCR.use_cassette('valuation') do
110
+ response = valuation.comps('zpid' => '48749425')
111
+ response.success?.should be_true
112
+ response.properties.should_not be_nil
113
+ end
114
+ end
115
+
116
+ it "should return a failure" do
117
+ VCR.use_cassette('valuation') do
118
+ response = valuation.comps('zpid' => 'xyz')
119
+ response.success?.should be_false
120
+ response.response_code.should == 500
121
+ response.error_message.should_not be_nil
122
+ response.should_not respond_to(:properties)
123
+ end
124
+ end
125
+ end
126
+
127
+ end
@@ -0,0 +1 @@
1
+ zws_id: you_zillow_api_key
data/zester.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "zester/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "zester"
7
+ s.version = Zester::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Tom Cocca"]
10
+ s.email = ["tom.cocca@gmail.com"]
11
+ s.homepage = "http://github.com/tcocca/zester"
12
+ s.summary = %q{Ruby wrapper for the Zillow API}
13
+ s.description = %q{Ruby API wrapper for Zillow API built with httparty designed for multiple keys}
14
+
15
+ s.rubyforge_project = "zester"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'multi_xml', '~> 0.5.0'
23
+ s.add_dependency 'httparty', '~> 0.8.3'
24
+ s.add_dependency "rash", "~> 0.3.2"
25
+ s.add_development_dependency "rake", "~> 0.9.2"
26
+ s.add_development_dependency "rspec", "~> 2.10.0"
27
+ s.add_development_dependency "webmock", "~> 1.8.6"
28
+ s.add_development_dependency "vcr", "~> 2.1.1"
29
+ end
metadata ADDED
@@ -0,0 +1,211 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zester
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Tom Cocca
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-09 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ type: :runtime
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 11
29
+ segments:
30
+ - 0
31
+ - 5
32
+ - 0
33
+ version: 0.5.0
34
+ version_requirements: *id001
35
+ name: multi_xml
36
+ - !ruby/object:Gem::Dependency
37
+ type: :runtime
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 57
45
+ segments:
46
+ - 0
47
+ - 8
48
+ - 3
49
+ version: 0.8.3
50
+ version_requirements: *id002
51
+ name: httparty
52
+ - !ruby/object:Gem::Dependency
53
+ type: :runtime
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 23
61
+ segments:
62
+ - 0
63
+ - 3
64
+ - 2
65
+ version: 0.3.2
66
+ version_requirements: *id003
67
+ name: rash
68
+ - !ruby/object:Gem::Dependency
69
+ type: :development
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 63
77
+ segments:
78
+ - 0
79
+ - 9
80
+ - 2
81
+ version: 0.9.2
82
+ version_requirements: *id004
83
+ name: rake
84
+ - !ruby/object:Gem::Dependency
85
+ type: :development
86
+ prerelease: false
87
+ requirement: &id005 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ~>
91
+ - !ruby/object:Gem::Version
92
+ hash: 39
93
+ segments:
94
+ - 2
95
+ - 10
96
+ - 0
97
+ version: 2.10.0
98
+ version_requirements: *id005
99
+ name: rspec
100
+ - !ruby/object:Gem::Dependency
101
+ type: :development
102
+ prerelease: false
103
+ requirement: &id006 !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ~>
107
+ - !ruby/object:Gem::Version
108
+ hash: 59
109
+ segments:
110
+ - 1
111
+ - 8
112
+ - 6
113
+ version: 1.8.6
114
+ version_requirements: *id006
115
+ name: webmock
116
+ - !ruby/object:Gem::Dependency
117
+ type: :development
118
+ prerelease: false
119
+ requirement: &id007 !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ hash: 9
125
+ segments:
126
+ - 2
127
+ - 1
128
+ - 1
129
+ version: 2.1.1
130
+ version_requirements: *id007
131
+ name: vcr
132
+ description: Ruby API wrapper for Zillow API built with httparty designed for multiple keys
133
+ email:
134
+ - tom.cocca@gmail.com
135
+ executables: []
136
+
137
+ extensions: []
138
+
139
+ extra_rdoc_files: []
140
+
141
+ files:
142
+ - .gitignore
143
+ - .rspec
144
+ - Gemfile
145
+ - LICENSE
146
+ - README.md
147
+ - Rakefile
148
+ - lib/zester.rb
149
+ - lib/zester/client.rb
150
+ - lib/zester/mortgage.rb
151
+ - lib/zester/neighborhood.rb
152
+ - lib/zester/property.rb
153
+ - lib/zester/resource.rb
154
+ - lib/zester/response.rb
155
+ - lib/zester/valuation.rb
156
+ - lib/zester/version.rb
157
+ - spec/spec_helper.rb
158
+ - spec/support/webmock_helper.rb
159
+ - spec/zester/client_spec.rb
160
+ - spec/zester/mortgage_spec.rb
161
+ - spec/zester/neighborhood_spec.rb
162
+ - spec/zester/property_spec.rb
163
+ - spec/zester/resource_spec.rb
164
+ - spec/zester/response_spec.rb
165
+ - spec/zester/valuation_spec.rb
166
+ - spec/zillow_api_key.yml.example
167
+ - zester.gemspec
168
+ homepage: http://github.com/tcocca/zester
169
+ licenses: []
170
+
171
+ post_install_message:
172
+ rdoc_options: []
173
+
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ hash: 3
182
+ segments:
183
+ - 0
184
+ version: "0"
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ none: false
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ hash: 3
191
+ segments:
192
+ - 0
193
+ version: "0"
194
+ requirements: []
195
+
196
+ rubyforge_project: zester
197
+ rubygems_version: 1.8.11
198
+ signing_key:
199
+ specification_version: 3
200
+ summary: Ruby wrapper for the Zillow API
201
+ test_files:
202
+ - spec/spec_helper.rb
203
+ - spec/support/webmock_helper.rb
204
+ - spec/zester/client_spec.rb
205
+ - spec/zester/mortgage_spec.rb
206
+ - spec/zester/neighborhood_spec.rb
207
+ - spec/zester/property_spec.rb
208
+ - spec/zester/resource_spec.rb
209
+ - spec/zester/response_spec.rb
210
+ - spec/zester/valuation_spec.rb
211
+ - spec/zillow_api_key.yml.example