vcs_client 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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