vcs_client 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,24 @@
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
+ ## BUNDLER
17
+ .bundle/
18
+
19
+ ## PROJECT::GENERAL
20
+ coverage
21
+ rdoc
22
+ pkg
23
+
24
+ ## PROJECT::SPECIFIC
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use default@vcs_client > /dev/null
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'httparty', '0.6.0'
4
+
5
+ group :test do
6
+ gem 'jeweler'
7
+ gem 'rspec', '1.3.0'
8
+ gem 'activesupport'
9
+ gem 'shoulda', '2.11.1'
10
+ gem 'bourne', '1.0'
11
+ gem 'ruby-debug'
12
+ gem 'webmock'
13
+ gem 'cucumber'
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,61 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (2.3.8)
5
+ addressable (2.1.2)
6
+ bourne (1.0)
7
+ mocha (= 0.9.8)
8
+ builder (2.1.2)
9
+ columnize (0.3.1)
10
+ crack (0.1.7)
11
+ cucumber (0.8.3)
12
+ builder (~> 2.1.2)
13
+ diff-lcs (~> 1.1.2)
14
+ gherkin (~> 2.0.2)
15
+ json_pure (~> 1.4.3)
16
+ term-ansicolor (~> 1.0.4)
17
+ diff-lcs (1.1.2)
18
+ gemcutter (0.5.0)
19
+ json_pure
20
+ gherkin (2.0.2)
21
+ trollop (~> 1.16.2)
22
+ git (1.2.5)
23
+ httparty (0.6.0)
24
+ crack (= 0.1.7)
25
+ jeweler (1.4.0)
26
+ gemcutter (>= 0.1.0)
27
+ git (>= 1.2.5)
28
+ rubyforge (>= 2.0.0)
29
+ json_pure (1.4.3)
30
+ linecache (0.43)
31
+ mocha (0.9.8)
32
+ rake
33
+ rake (0.8.7)
34
+ rspec (1.3.0)
35
+ ruby-debug (0.10.3)
36
+ columnize (>= 0.1)
37
+ ruby-debug-base (~> 0.10.3.0)
38
+ ruby-debug-base (0.10.3)
39
+ linecache (>= 0.3)
40
+ rubyforge (2.0.4)
41
+ json_pure (>= 1.1.7)
42
+ shoulda (2.11.1)
43
+ term-ansicolor (1.0.5)
44
+ trollop (1.16.2)
45
+ webmock (1.3.0)
46
+ addressable (>= 2.1.1)
47
+ crack (>= 0.1.7)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ activesupport
54
+ bourne (= 1.0)
55
+ cucumber
56
+ httparty (= 0.6.0)
57
+ jeweler
58
+ rspec (= 1.3.0)
59
+ ruby-debug
60
+ shoulda (= 2.11.1)
61
+ webmock
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 gill
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.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = vcs_client
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 gill. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,143 @@
1
+ APP_NAME = 'vcs_client'
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "vcs_client"
10
+ gem.summary = %Q{This gem wraps the Voter Checkout Service API}
11
+ gem.description = %Q{This gem provides all the plumbing to interact with the Voter Checkout Service}
12
+ gem.email = "gilltots@gmail.com"
13
+ gem.homepage = "http://github.com/gilltots/vcs_client"
14
+ gem.authors = ["gill"]
15
+ gem.add_development_dependency "shoulda", ">= 0"
16
+ gem.add_dependency "httparty", ">= 0.5.2"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ task :default => :spec
25
+
26
+ task :test => :check_dependencies
27
+
28
+ require 'spec/rake/spectask'
29
+
30
+ desc "Run all examples"
31
+ Spec::Rake::SpecTask.new('spec') do |t|
32
+ t.spec_files = FileList['spec/**/*.rb']
33
+ # t.spec_opts = ['--debugger']
34
+ end
35
+
36
+ desc "Cruise Control task"
37
+ task :cruise do
38
+ gem 'activesupport', '2.3.8'
39
+ require 'metric_fu'
40
+ MetricFu::Configuration.run do |config|
41
+ # Define which metrics you want to use
42
+ config.metrics = [:rcov]
43
+ config.graphs = [:rcov]
44
+ config.rcov = {
45
+ :environment => 'test',
46
+ :test_files => ['spec/**/*_spec.rb'],
47
+ :rcov_opts => ["-Ilib:spec --aggregate rcov_data/rcov.data --sort coverage --no-html --profile --no-color --text-coverage --exclude /gems/,/Library/,/rubygems/,spec,/usr/,script,features/ "]}
48
+ end
49
+
50
+ path = File.expand_path(File.join(File.dirname(__FILE__)))
51
+ `mkdir -p #{path}/rcov_data/`
52
+ `rm #{path}/rcov_data/rcov.data` rescue nil
53
+ `rm #{path}/rerun.txt` rescue nil
54
+ # Even though metrics:all runs the specs under rcov, it doesn't report any failures, which is NOT what we want!
55
+ CruiseControl::invoke_rake_task 'spec'
56
+ CruiseControl::invoke_rake_task 'features'
57
+ CruiseControl::invoke_rake_task 'metrics:all'
58
+ # CruiseControl::invoke_rake_task 'verify_rcov'
59
+ end
60
+
61
+ task :local_metrics do
62
+ require 'metric_fu'
63
+ MetricFu::Configuration.run do |config|
64
+ # Define which metrics you want to use
65
+ config.metrics = [:rcov]
66
+ config.graphs = [:rcov]
67
+ config.rcov = {
68
+ :test_files => ['spec/**/*_spec.rb'],
69
+ :rcov_opts => [
70
+ "--sort coverage",
71
+ "--no-html",
72
+ "--text-coverage",
73
+ "--no-color",
74
+ "--profile",
75
+ "--exclude /gems/,Library/,spec/"
76
+ ]
77
+ }
78
+ end
79
+
80
+ Rake::Task['metrics:all'].invoke
81
+ end
82
+
83
+
84
+ def get_coverage(file_location)
85
+ total_coverage = 0
86
+ File.open(file_location).each_line do |line|
87
+ if line =~ /Total Coverage: (\d+\.\d+)%\s/
88
+ total_coverage = $1.to_f
89
+ break
90
+ end
91
+ end
92
+ return total_coverage
93
+ end
94
+
95
+ task :verify_rcov do
96
+ project_root = "/home/deploy/.cruise/projects/#{APP_NAME}"
97
+ html_location = "output/rcov.html"
98
+ out = ENV['CC_BUILD_ARTIFACTS']
99
+
100
+ # Grab the last rcov level from a successful build (unsuccessful may not have created the file)
101
+ last_build = Dir.new(project_root).entries.select{|f| f =~ /success/}.sort{|a, b| File.mtime(File.join(project_root, a)) <=> File.mtime(File.join(project_root, b))}.last
102
+
103
+ threshold = last_build.nil? ? 0 : get_coverage(File.join(project_root, last_build, html_location))
104
+ total_coverage = get_coverage(File.join(out, html_location))
105
+
106
+ puts "Coverage: #{total_coverage}% (threshold: #{threshold}% from #{last_build})"
107
+ raise "Coverage must be at least #{threshold}% (from #{last_build}) but was #{total_coverage}%" if total_coverage < threshold
108
+
109
+ end
110
+
111
+ require 'rake/rdoctask'
112
+ Rake::RDocTask.new do |rdoc|
113
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
114
+
115
+ rdoc.rdoc_dir = 'rdoc'
116
+ rdoc.title = "vcs_client #{version}"
117
+ rdoc.rdoc_files.include('README*')
118
+ rdoc.rdoc_files.include('lib/**/*.rb')
119
+ end
120
+
121
+ begin
122
+ require 'rcov/rcovtask'
123
+ Spec::Rake::SpecTask.new('rcov') do |t|
124
+ t.spec_files = FileList['spec/**/*.rb']
125
+ t.rcov = true
126
+ t.rcov_opts = ['--exclude', 'spec,/gems/']
127
+ end
128
+ rescue LoadError
129
+ task :rcov do
130
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
131
+ end
132
+ end
133
+
134
+ begin
135
+ require 'cucumber/rake/task'
136
+ Cucumber::Rake::Task.new(:features)
137
+
138
+ task :features => :check_dependencies
139
+ rescue LoadError
140
+ task :features do
141
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
142
+ end
143
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.6
@@ -0,0 +1,36 @@
1
+ Given /^the vcs uri is set to "([^"]*)"$/ do |uri|
2
+ @uri = uri
3
+ VcsClient.vcs_uri = @uri
4
+ end
5
+
6
+ When /^I ask for a random voter from the "([^"]*)" campaign$/ do |campaign_id|
7
+ @client_id = rand(500)
8
+ @volunteer_id = rand(500)
9
+ @campaign_id = campaign_id
10
+ @voters = VcsClient.checkout(@campaign_id, @client_id, @volunteer_id)
11
+ end
12
+
13
+ Then /^I should get a valid collection of people$/ do
14
+ @voters.should be_kind_of(Array)
15
+ @voters.each do |person|
16
+ person.should be_kind_of(VcsClient::Person)
17
+ end
18
+ end
19
+
20
+ When /^I ask for a random voter from the "([^"]*)" campaign at "([^"]*)" and "([^"]*)" withing "([^"]*)" miles$/ do |campaign_id, latitude, longitude, radius|
21
+ @client_id = rand(500)
22
+ @volunteer_id = rand(500)
23
+ @campaign_id = campaign_id
24
+ @voters = VcsClient.checkout(@campaign_id, @client_id, @volunteer_id, latitude, longitude, radius)
25
+ end
26
+
27
+ When /^I check the voters in$/ do
28
+ @voters.each do |person|
29
+ person.checkin(:disposition => "1", :response => "Something Happened")
30
+ end
31
+ end
32
+
33
+ Then /^I should have zero checkouts$/ do
34
+ response = HTTParty.get("#{@uri}/campaign/#{@campaign_id}/client/#{@client_id}/volunteer/#{@volunteer_id}/checkout", :format => :json)
35
+ response["campaign_#{@campaign_id}"].should == 0
36
+ end
@@ -0,0 +1,6 @@
1
+ require 'ruby-debug'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
4
+ require 'vcs_client'
5
+
6
+ require 'spec/expectations'
@@ -0,0 +1,17 @@
1
+ Feature: Vcs client
2
+
3
+ Background:
4
+ Given the vcs uri is set to "http://vcsqa.dnc.org:8080"
5
+
6
+ Scenario: Getting a random voter
7
+ When I ask for a random voter from the "perf" campaign
8
+ Then I should get a valid collection of people
9
+
10
+ Scenario: Getting a random voter in a specific location
11
+ When I ask for a random voter from the "perf" campaign at "38.85" and "-77.04" withing "5" miles
12
+ Then I should get a valid collection of people
13
+
14
+ Scenario: Checking in a checked out voter
15
+ When I ask for a random voter from the "perf" campaign
16
+ And I check the voters in
17
+ Then I should have zero checkouts
@@ -0,0 +1,7 @@
1
+ module VcsClient
2
+
3
+ class Action < Basement
4
+ attr_accessor :name, :method, :url
5
+ end
6
+
7
+ end
@@ -0,0 +1,20 @@
1
+ module VcsClient
2
+
3
+ class Address < Basement
4
+ attr_accessor :zip4, :latitude, :longitude, :zip, :city, :state, :address, :id, :type
5
+
6
+ # TODO: Once the Schema is updated for the spelling correction these aliases can be removed
7
+ alias :zipp4 :zip4
8
+ alias :zipp4= :zip4=
9
+
10
+ def latitude=(latitude)
11
+ @latitude = latitude.to_f
12
+ end
13
+
14
+ def longitude=(longitude)
15
+ @longitude = longitude.to_f
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,15 @@
1
+ module VcsClient
2
+
3
+ class Basement
4
+
5
+ def initialize(attrs = {})
6
+ attrs.each do |key,value|
7
+ if self.respond_to?("#{key.to_s}=")
8
+ self.send("#{key.to_s}=", value)
9
+ end
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,96 @@
1
+ require 'date'
2
+
3
+ module VcsClient
4
+
5
+ class Person < Basement
6
+
7
+ attr_accessor :precinct, :cong_dist, :first_name, :last_name, :middle_name, :sex, :date_of_birth, :univ, :party, :actions, :address, :phone, :references
8
+
9
+ def address=(_address)
10
+ @address = VcsClient::Address.new(_address) if _address
11
+ end
12
+
13
+ def phone=(_phone)
14
+ @phone = VcsClient::Phone.new(_phone) if _phone
15
+ end
16
+
17
+ def token
18
+ if self.actions && self.actions.key?("checkin")
19
+ self.actions["checkin"].url.split('/').last
20
+ end
21
+ end
22
+
23
+ def references=(references)
24
+ if references
25
+ @references = references.inject([]) do |collection, reference|
26
+ collection << VcsClient::Reference.new(reference)
27
+ end
28
+ end
29
+ end
30
+
31
+ def age
32
+ if date_of_birth
33
+ date = Date.today
34
+ day_diff = date.day - date_of_birth.day
35
+ month_diff = date.month - date_of_birth.month - (day_diff < 0 ? 1 : 0)
36
+ date.year - date_of_birth.year - (month_diff < 0 ? 1 : 0)
37
+ end
38
+ end
39
+
40
+ def name
41
+ if @name.nil?
42
+ @name = [self.first_name, self.middle_name, self.last_name].compact.join(' ')
43
+ end
44
+ @name
45
+ end
46
+
47
+ def date_of_birth=(_date_of_birth)
48
+ @date_of_birth = case _date_of_birth
49
+ when /^\d{4}-\d{1,2}-\d{1,2}$/
50
+ Date.parse(_date_of_birth)
51
+ when /^\d{4}$/
52
+ Date.parse("#{_date_of_birth}-01-01")
53
+ else
54
+ nil
55
+ end
56
+ end
57
+
58
+ # this dynamically creates a method on this object, named after the action name ('checkin')
59
+ # e.g. if the json has
60
+ # "actions": {
61
+ # "checkin": {
62
+ # "method": "PUT",
63
+ # "url": "/campaign/1234/client/1/volunteer/4321/checkout/4bfd8e5e8a9b8e7987000014"
64
+ # }
65
+ # },
66
+ #
67
+ # this adds a method called "checkin" to this object, that takes a single parameter for 'disposition' and
68
+ # performs a PUT to /campaign/1234/client/1/volunteer/4321/checkout/4bfd8e5e8a9b8e7987000014
69
+ #
70
+ # it also adds an Action object to the person, so calling this_person.actions will return a hash of all actions, where the keys
71
+ # are the action names and the values are Action objects with url and method
72
+ #
73
+ def actions=(actions)
74
+ @actions = actions.inject({}) do |collection, action|
75
+ name = action[0]
76
+ options = action[1]
77
+ define_meta_action(name, options)
78
+ collection.merge!(name => VcsClient::Action.new({:name => name}.merge(options)))
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def define_meta_action(name, options)
85
+ # The following defines a metaclass method on the instance
86
+ method = <<-METHOD
87
+ def self.#{name}(args = {})
88
+ VcsClient.send(:#{options["method"].downcase}, '#{options["url"]}', :body => args, :format => :html)
89
+ end
90
+ METHOD
91
+ send(:eval, method)
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,7 @@
1
+ module VcsClient
2
+
3
+ class Phone < Basement
4
+ attr_accessor :number, :type
5
+ end
6
+
7
+ end
@@ -0,0 +1,8 @@
1
+ module VcsClient
2
+
3
+ class Reference < Basement
4
+ attr_accessor :src, :id
5
+
6
+ end
7
+
8
+ end
data/lib/vcs_client.rb ADDED
@@ -0,0 +1,79 @@
1
+ require 'httparty'
2
+ require 'objects/basement'
3
+ require 'objects/action'
4
+ require 'objects/address'
5
+ require 'objects/phone'
6
+ require 'objects/reference'
7
+ require 'objects/person'
8
+
9
+ module VcsClient
10
+
11
+ include HTTParty
12
+
13
+ format :json
14
+
15
+ def self.vcs_uri=(uri)
16
+ base_uri uri
17
+ end
18
+
19
+ def self.checkout(campaign_id, client_id, volunteer_id, lat = nil, long = nil, miles = nil)
20
+ query_type = 'random'
21
+ body = {'queryType' => query_type}
22
+ if (!lat.nil? && !long.nil?)
23
+ query_type = 'location'
24
+ if miles.nil?
25
+ raise VcsClient::NoRadius.new("When searching by location a radius is necessary")
26
+ end
27
+
28
+ body = {'queryType' => query_type, 'latitude' => lat, 'longitude' => long, 'miles' => miles}
29
+ end
30
+ result = post("/campaign/#{campaign_id}/client/#{client_id}/volunteer/#{volunteer_id}/checkout", :body => body)
31
+ filter_for_errors(result)
32
+ result["people"].inject([]) do |collection, person|
33
+ collection << Person.new(person)
34
+ end
35
+ end
36
+
37
+ def self.checkin(campaign_id, client_id, vol_id, token, disposition)
38
+ result = put("/campaign/#{campaign_id}/client/#{client_id}/volunteer/#{vol_id}/checkout/#{token}", :body => {'disposition' => disposition}, :format => "html")
39
+ filter_for_checkin_errors(result)
40
+ Crack::JSON.parse(result)
41
+ end
42
+
43
+ private
44
+
45
+ def self.filter_for_errors(hash)
46
+ if hash.nil? || hash["people"].blank?
47
+ raise VcsClient::CheckoutFailed.new(hash)
48
+ end
49
+ hash
50
+ end
51
+
52
+ def self.filter_for_checkin_errors(result)
53
+ if result == "Could not find an available Person"
54
+ raise VcsClient::CheckinFailed.new(result)
55
+ end
56
+ end
57
+
58
+ class VcsError < StandardError
59
+ attr_accessor :response
60
+
61
+ def initialize(val)
62
+ @response = val
63
+ end
64
+
65
+ def to_s
66
+ "#{self.class.to_s}: #{self.response}"
67
+ end
68
+
69
+ def inspect
70
+ self.to_s
71
+ end
72
+
73
+ end
74
+
75
+ class CheckinFailed < VcsError; end
76
+ class CheckoutFailed < VcsError; end
77
+ class NoRadius < VcsError; end
78
+
79
+ end
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe VcsClient::Action do
4
+
5
+ describe 'creating from attrs' do
6
+ subject do
7
+ VcsClient::Action.new({'name' => 'checkin', 'method' => 'PUT', 'url' => '/some/thing'})
8
+ end
9
+
10
+ its(:name) { should == 'checkin' }
11
+ its(:method) { should == 'PUT' }
12
+ its(:url) { should == '/some/thing' }
13
+ end
14
+
15
+ end
@@ -0,0 +1,21 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe VcsClient::Address do
4
+
5
+ describe 'creating from attrs' do
6
+ subject do
7
+ VcsClient::Address.new(Crack::JSON.parse(one_person_fixture)['people'].first['address'])
8
+ end
9
+
10
+ its(:address) { should == '1234 ADDRESS ST' }
11
+ its(:city) { should == 'CITY' }
12
+ its(:zip) { should == '00000' }
13
+ its(:id) { should == 123456789 }
14
+ its(:type) { should == 'r' }
15
+ its(:zip4) { should == '0000' }
16
+ its(:state) { should == 'TN' }
17
+ its(:latitude) { should == 0.0 }
18
+ its(:longitude) { should == -0.0 }
19
+ end
20
+
21
+ end
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe VcsClient::Basement do
4
+
5
+ describe 'creating from attrs' do
6
+
7
+ subject do
8
+ VcsClient::Basement.new({:lat => 123, :long => 456})
9
+ end
10
+
11
+ it 'should not set anything, but also not bomb' do
12
+ (subject.methods - Object.methods).should be_empty
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ {
2
+ "person": "XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX",
3
+ "disposition": "1",
4
+ "campaign": "test",
5
+ "checkout_id": "abcdef1234567890",
6
+ "response": "Checkin Successful"
7
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "people": [
3
+ {
4
+ "cong_dist": 1,
5
+ "party": "I",
6
+ "precinct": null,
7
+ "univ": 2,
8
+ "actions": {
9
+ "checkin": {
10
+ "method": "PUT",
11
+ "url": "/campaign/abcd/client/1234/volunteer/5678/checkout/abcdefg12345678"
12
+ }
13
+ },
14
+ "address": {
15
+ "zipp4": "0000",
16
+ "state": "TN",
17
+ "zip": "00000",
18
+ "city": "CITY",
19
+ "address": "1234 ADDRESS ST",
20
+ "id": 123456789,
21
+ "type": "r",
22
+ "latitude": "00.000000",
23
+ "longitude": "-00.000000"
24
+ },
25
+ "date_of_birth": "1980-3-20",
26
+ "first_name": "JOHN",
27
+ "last_name": "DOE",
28
+ "middle_name": "Q",
29
+ "phone": {
30
+ "type": "L",
31
+ "number": "5555555555"
32
+ },
33
+ "references": [
34
+ {
35
+ "src": "V",
36
+ "id": "AA0000000"
37
+ }
38
+ ],
39
+ "sex": "M",
40
+ "id": "N-00000000-00AA-AAAA-0000-000000000000"
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "people": [
3
+ {
4
+ "cong_dist": 1,
5
+ "party": "I",
6
+ "precinct": null,
7
+ "univ": 2,
8
+ "actions": {
9
+ "checkin": {
10
+ "method": "PUT",
11
+ "url": "/campaign/abcd/client/1234/volunteer/5678/checkout/abcdefg12345678"
12
+ }
13
+ },
14
+ "address": {
15
+ "zipp4": "0000",
16
+ "state": "TN",
17
+ "zip": "00000",
18
+ "city": "CITY",
19
+ "address": "1234 ADDRESS ST",
20
+ "id": 123456789,
21
+ "type": "r",
22
+ "latitude": "00.000000",
23
+ "longitude": "00.000000"
24
+ },
25
+ "date_of_birth": "1980-3-20",
26
+ "first_name": "JOHN",
27
+ "last_name": "DOE",
28
+ "middle_name": "Q",
29
+ "phone": {
30
+ "type": "L",
31
+ "number": "5555555555"
32
+ },
33
+ "references": [
34
+ {
35
+ "src": "V",
36
+ "id": "AA0000000"
37
+ }
38
+ ],
39
+ "sex": "M",
40
+ "id": "N-00000000-00AA-AAAA-0000-000000000000"
41
+ },
42
+ {
43
+ "cong_dist": 1,
44
+ "party": "I",
45
+ "precinct": null,
46
+ "univ": 2,
47
+ "actions": {
48
+ "checkin": {
49
+ "method": "PUT",
50
+ "url": "/campaign/abcd/client/1234/volunteer/5678/checkout/aaaaa99999"
51
+ }
52
+ },
53
+ "address": {
54
+ "zipp4": "0000",
55
+ "state": "TN",
56
+ "zip": "00000",
57
+ "city": "CITY",
58
+ "address": "1234 ADDRESS ST",
59
+ "id": 123456789,
60
+ "type": "r",
61
+ "latitude": "00.000000",
62
+ "longitude": "00.000000"
63
+ },
64
+ "date_of_birth": "1981-2-17",
65
+ "first_name": "JANE",
66
+ "last_name": "DOE",
67
+ "middle_name": "P",
68
+ "phone": {
69
+ "type": "L",
70
+ "number": "5555555555"
71
+ },
72
+ "references": [
73
+ {
74
+ "src": "V",
75
+ "id": "BB0000000"
76
+ }
77
+ ],
78
+ "sex": "F",
79
+ "id": "N-00000000-00BB-BBBB-0000-000000000000"
80
+ }
81
+
82
+ ]
83
+ }
@@ -0,0 +1,92 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe VcsClient::Person do
4
+
5
+ describe 'creating from attrs' do
6
+
7
+ subject do
8
+ VcsClient::Person.new(Crack::JSON.parse(one_person_fixture)['people'].first)
9
+ end
10
+
11
+ its(:precinct) { should be_nil }
12
+ its(:cong_dist) { should == 1 }
13
+ its(:first_name) { should == 'JOHN' }
14
+ its(:last_name) { should == 'DOE' }
15
+ its(:middle_name) { should == 'Q' }
16
+ its(:sex) { should == 'M' }
17
+ its(:univ) { should == 2 }
18
+ its(:party) { should == 'I' }
19
+ its(:date_of_birth) { should == Date.parse('1980-3-20') }
20
+ its(:address) { should be_instance_of(VcsClient::Address) }
21
+ its(:phone) { should be_instance_of(VcsClient::Phone) }
22
+ its(:token) { should == 'abcdefg12345678' }
23
+
24
+ it 'should return a nil token if no checkin action exists' do
25
+ VcsClient::Person.new.token.should be_nil
26
+ end
27
+
28
+ it 'should have an age accessor that uses the date_of_birth' do
29
+ Date.stubs(:today).returns(Date.parse('2010-3-20'))
30
+ subject.age.should == 30
31
+ end
32
+
33
+ it 'should have an array of 1 reference' do
34
+ subject.references.class.should == Array
35
+ subject.references.length.should == 1
36
+ subject.references.first.should be_instance_of(VcsClient::Reference)
37
+ end
38
+
39
+ it 'should have keyed hash for actions' do
40
+ subject.actions.class.should == Hash
41
+ subject.actions['checkin'].class.should == VcsClient::Action
42
+ end
43
+
44
+ it 'should create a dynamic method called "checkin" that does a PUT' do
45
+ VcsClient.stubs(:put)
46
+ subject.checkin('')
47
+ VcsClient.should have_received(:put)
48
+ end
49
+
50
+ end
51
+
52
+ context 'nil responses' do
53
+ describe 'phone' do
54
+ subject { VcsClient::Person.new(:phone => nil, :address => nil, :date_of_birth => nil) }
55
+ its(:phone) { should be_nil }
56
+ its(:address) { should be_nil }
57
+ its(:date_of_birth) { should be_nil }
58
+ its(:age) { should be_nil }
59
+ end
60
+ end
61
+
62
+ context 'nil references' do
63
+ it 'should not raise an error' do
64
+ expect {
65
+ VcsClient::Person.new(:references => nil)
66
+ }.to_not raise_error
67
+ end
68
+ end
69
+
70
+ context 'only getting a year back for the date_of_birth' do
71
+ subject { VcsClient::Person.new(:date_of_birth => "1980") }
72
+ its(:date_of_birth) { should == Date.parse("1980-01-01") }
73
+ end
74
+
75
+ describe 'proper spacing for #name' do
76
+ it 'should properly display the full name when all 3 are given' do
77
+ person = VcsClient::Person.new(:first_name => "First", :middle_name => "Middle", :last_name => "Last")
78
+ person.name.should == "First Middle Last"
79
+ end
80
+
81
+ it 'should properly display the full name when only first and last are given' do
82
+ person = VcsClient::Person.new(:first_name => "First", :last_name => "Last")
83
+ person.name.should == "First Last"
84
+ end
85
+
86
+ it 'should properly display the full name when only first is given' do
87
+ person = VcsClient::Person.new(:first_name => "First")
88
+ person.name.should == "First"
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe VcsClient::Phone do
4
+
5
+ describe 'creating from attrs' do
6
+
7
+ subject do
8
+ VcsClient::Phone.new(Crack::JSON.parse(one_person_fixture)['people'].first['phone'])
9
+ end
10
+
11
+ its(:number) { should == '5555555555' }
12
+ its(:type) { should == 'L' }
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe VcsClient::Reference do
4
+
5
+ describe 'creating from attrs' do
6
+ subject do
7
+ VcsClient::Reference.new({:src => 'source', :id => 456})
8
+ end
9
+
10
+ its(:src) { should == 'source' }
11
+ its(:id) { should == 456 }
12
+ end
13
+
14
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+
3
+ require 'bundler'
4
+ Bundler.setup
5
+
6
+ require 'spec'
7
+ require 'shoulda'
8
+ require 'bourne'
9
+ require 'spec/autorun'
10
+ require 'ruby-debug'
11
+ require 'webmock/rspec'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+
16
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
17
+
18
+ require 'vcs_client'
19
+
20
+ Spec::Runner.configure do |config|
21
+ config.mock_with Mocha::API
22
+ config.include WebMock
23
+ config.include Fixtures
24
+ end
@@ -0,0 +1,19 @@
1
+ module Fixtures
2
+ def self.included(base)
3
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'../fixtures','**','*.*'))].each do |file|
4
+ file_name = file.split('/').last
5
+ method_name = file_name.split('.').first
6
+ method = <<-METHOD
7
+ def #{method_name}_fixture
8
+ unless @#{method_name}_fixture
9
+ fixture_file = File.new('#{file}', 'r')
10
+ @#{method_name}_fixture = fixture_file.read
11
+ fixture_file.close
12
+ end
13
+ @#{method_name}_fixture
14
+ end
15
+ METHOD
16
+ eval(method)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module Mocha
2
+ module API
3
+ def setup_mocks_for_rspec
4
+ mocha_setup
5
+ end
6
+ def verify_mocks_for_rspec
7
+ mocha_verify
8
+ end
9
+ def teardown_mocks_for_rspec
10
+ mocha_teardown
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,83 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe VcsClient do
4
+
5
+ describe 'setting the base_uri' do
6
+ before do
7
+ VcsClient.stubs(:base_uri)
8
+ end
9
+
10
+ it "should set HTTParty's base_uri" do
11
+ VcsClient.vcs_uri = 'http://test.uri.com:8080'
12
+ VcsClient.should have_received(:base_uri).with('http://test.uri.com:8080')
13
+ end
14
+ end
15
+
16
+ context 'with a fake VCS URI set' do
17
+ before do
18
+ VcsClient.vcs_uri = 'http://test.uri.com:8080'
19
+ end
20
+
21
+ describe 'checking out, stubbed' do
22
+ describe 'succesfully' do
23
+ it 'should work' do
24
+ stub_request(:post, /.+/).to_return(:body => two_people_fixture)
25
+ people = VcsClient.checkout('perf', rand(500), rand(500))
26
+ people.class.should == Array
27
+ people.length.should == 2
28
+ people.each do |person|
29
+ person.respond_to?(:checkin).should == true
30
+ end
31
+ end
32
+ end
33
+
34
+ describe 'successfully with lat/long info' do
35
+ it 'should also work' do
36
+ stub_request(:post, /.+/).to_return(:body => two_people_fixture)
37
+ people = VcsClient.checkout('perf', rand(500), rand(500), 38.883966, -77.008603, 5)
38
+ people.class.should == Array
39
+ people.length.should == 2
40
+ people.each do |person|
41
+ person.respond_to?(:checkin).should == true
42
+ end
43
+ end
44
+ end
45
+
46
+ describe 'if only lat and lon are given' do
47
+ it 'should raise an Exception for missing radius' do
48
+ expect {
49
+ VcsClient.checkout('perf', rand(500), rand(500), 0.0, -77.0)
50
+ }.to raise_error(VcsClient::NoRadius) { |exception| exception.inspect.should == 'VcsClient::NoRadius: When searching by location a radius is necessary' }
51
+ end
52
+ end
53
+
54
+ describe 'unsuccessfully' do
55
+ it 'should bomb' do
56
+ stub_request(:post, /.+/).to_return(:body => '{}')
57
+ expect {
58
+ VcsClient.checkout('perf',2,3)
59
+ }.to raise_error(VcsClient::CheckoutFailed){|ex| ex.inspect.should == 'VcsClient::CheckoutFailed: '}
60
+ end
61
+ end
62
+ end
63
+
64
+ describe 'checking in, stubbed' do
65
+ describe 'succesfully' do
66
+ it 'should work' do
67
+ stub_request(:put, /.+/).to_return(:body => checkin_response_fixture)
68
+ VcsClient.checkin(1,2,3,4,5)['response'].should == 'Checkin Successful'
69
+ end
70
+ end
71
+
72
+ describe 'unsuccessfully' do
73
+ it 'should bomb' do
74
+ stub_request(:put, /.+/).to_return(:body => 'Could not find an available Person')
75
+ expect {
76
+ VcsClient.checkin(1,2,3,4,5)
77
+ }.to raise_error(VcsClient::CheckinFailed){|ex| ex.inspect.should == 'VcsClient::CheckinFailed: Could not find an available Person' }
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,87 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{vcs_client}
8
+ s.version = "0.4.6"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["gill"]
12
+ s.date = %q{2010-07-13}
13
+ s.description = %q{This gem provides all the plumbing to interact with the Voter Checkout Service}
14
+ s.email = %q{gilltots@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ ".rvmrc",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "features/step_definitions/vcs_client_steps.rb",
29
+ "features/support/env.rb",
30
+ "features/vcs_client.feature",
31
+ "lib/objects/action.rb",
32
+ "lib/objects/address.rb",
33
+ "lib/objects/basement.rb",
34
+ "lib/objects/person.rb",
35
+ "lib/objects/phone.rb",
36
+ "lib/objects/reference.rb",
37
+ "lib/vcs_client.rb",
38
+ "spec/action_spec.rb",
39
+ "spec/address_spec.rb",
40
+ "spec/basement_spec.rb",
41
+ "spec/fixtures/checkin_response.json",
42
+ "spec/fixtures/one_person.json",
43
+ "spec/fixtures/two_people.json",
44
+ "spec/person_spec.rb",
45
+ "spec/phone_spec.rb",
46
+ "spec/reference_spec.rb",
47
+ "spec/spec_helper.rb",
48
+ "spec/support/fixtures.rb",
49
+ "spec/support/mocha.rb",
50
+ "spec/vcs_client_spec.rb",
51
+ "vcs_client.gemspec"
52
+ ]
53
+ s.homepage = %q{http://github.com/gilltots/vcs_client}
54
+ s.rdoc_options = ["--charset=UTF-8"]
55
+ s.require_paths = ["lib"]
56
+ s.rubygems_version = %q{1.3.6}
57
+ s.summary = %q{This gem wraps the Voter Checkout Service API}
58
+ s.test_files = [
59
+ "spec/action_spec.rb",
60
+ "spec/address_spec.rb",
61
+ "spec/basement_spec.rb",
62
+ "spec/person_spec.rb",
63
+ "spec/phone_spec.rb",
64
+ "spec/reference_spec.rb",
65
+ "spec/spec_helper.rb",
66
+ "spec/support/fixtures.rb",
67
+ "spec/support/mocha.rb",
68
+ "spec/vcs_client_spec.rb"
69
+ ]
70
+
71
+ if s.respond_to? :specification_version then
72
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
73
+ s.specification_version = 3
74
+
75
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
76
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
77
+ s.add_runtime_dependency(%q<httparty>, [">= 0.5.2"])
78
+ else
79
+ s.add_dependency(%q<shoulda>, [">= 0"])
80
+ s.add_dependency(%q<httparty>, [">= 0.5.2"])
81
+ end
82
+ else
83
+ s.add_dependency(%q<shoulda>, [">= 0"])
84
+ s.add_dependency(%q<httparty>, [">= 0.5.2"])
85
+ end
86
+ end
87
+
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vcs_client
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 4
8
+ - 6
9
+ version: 0.4.6
10
+ platform: ruby
11
+ authors:
12
+ - gill
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-13 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: httparty
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ - 5
42
+ - 2
43
+ version: 0.5.2
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ description: This gem provides all the plumbing to interact with the Voter Checkout Service
47
+ email: gilltots@gmail.com
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ extra_rdoc_files:
53
+ - LICENSE
54
+ - README.rdoc
55
+ files:
56
+ - .gitignore
57
+ - .rvmrc
58
+ - Gemfile
59
+ - Gemfile.lock
60
+ - LICENSE
61
+ - README.rdoc
62
+ - Rakefile
63
+ - VERSION
64
+ - features/step_definitions/vcs_client_steps.rb
65
+ - features/support/env.rb
66
+ - features/vcs_client.feature
67
+ - lib/objects/action.rb
68
+ - lib/objects/address.rb
69
+ - lib/objects/basement.rb
70
+ - lib/objects/person.rb
71
+ - lib/objects/phone.rb
72
+ - lib/objects/reference.rb
73
+ - lib/vcs_client.rb
74
+ - spec/action_spec.rb
75
+ - spec/address_spec.rb
76
+ - spec/basement_spec.rb
77
+ - spec/fixtures/checkin_response.json
78
+ - spec/fixtures/one_person.json
79
+ - spec/fixtures/two_people.json
80
+ - spec/person_spec.rb
81
+ - spec/phone_spec.rb
82
+ - spec/reference_spec.rb
83
+ - spec/spec_helper.rb
84
+ - spec/support/fixtures.rb
85
+ - spec/support/mocha.rb
86
+ - spec/vcs_client_spec.rb
87
+ - vcs_client.gemspec
88
+ has_rdoc: true
89
+ homepage: http://github.com/gilltots/vcs_client
90
+ licenses: []
91
+
92
+ post_install_message:
93
+ rdoc_options:
94
+ - --charset=UTF-8
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.3.6
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: This gem wraps the Voter Checkout Service API
118
+ test_files:
119
+ - spec/action_spec.rb
120
+ - spec/address_spec.rb
121
+ - spec/basement_spec.rb
122
+ - spec/person_spec.rb
123
+ - spec/phone_spec.rb
124
+ - spec/reference_spec.rb
125
+ - spec/spec_helper.rb
126
+ - spec/support/fixtures.rb
127
+ - spec/support/mocha.rb
128
+ - spec/vcs_client_spec.rb