nationbuilder-rb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,97 @@
1
+ class NationBuilder::Client
2
+
3
+ def initialize(nation_name, api_key)
4
+ @nation_name = nation_name
5
+ @api_key = api_key
6
+ @name_to_endpoint = {}
7
+ parsed_endpoints.each do |endpoint|
8
+ @name_to_endpoint[endpoint.name] = endpoint
9
+ end
10
+ end
11
+
12
+ def parsed_endpoints
13
+ NationBuilder::SpecParser
14
+ .parse(File.join(File.dirname(__FILE__), 'api_spec.json'))
15
+ end
16
+
17
+ def [](endpoint)
18
+ @name_to_endpoint[endpoint]
19
+ end
20
+
21
+ def endpoints
22
+ @name_to_endpoint.keys
23
+ end
24
+
25
+ def base_url
26
+ @base_url ||=
27
+ NationBuilder::URL_TEMPLATE.gsub(':nation_name', @nation_name)
28
+ end
29
+
30
+ def call(endpoint_name, method_name, args={})
31
+ endpoint = self[endpoint_name]
32
+ method = endpoint[method_name]
33
+ nonmethod_args = method.nonmethod_args(args)
34
+ method_args = method.method_args(args)
35
+ method.validate_args(method_args)
36
+ url = NationBuilder.generate_url(base_url, method.uri, method_args)
37
+
38
+ request_args = {
39
+ header: {
40
+ 'Accept' => 'application/json',
41
+ 'Content-Type' => 'application/json'
42
+ }
43
+ }
44
+
45
+ if method.http_method == :get
46
+ request_args[:query] = nonmethod_args
47
+ request_args[:query][:access_token] = @api_key
48
+ else
49
+ nonmethod_args[:access_token] = @api_key
50
+ request_args[:body] = JSON(nonmethod_args)
51
+ end
52
+
53
+ response = HTTPClient.send(method.http_method, url, request_args)
54
+ return parse_response_body(response)
55
+ end
56
+
57
+ class ServerResponseError < StandardError; end
58
+
59
+ def parse_response_body(response)
60
+ success = response.code.to_s.start_with?('2')
61
+
62
+ if response.header['Content-Type'].first != 'application/json'
63
+ return {} if success
64
+ raise ServerResponseError.new("Non-JSON content-type for server response: #{response.body}")
65
+ end
66
+
67
+ body = response.body.strip
68
+ return {} if body.length == 0
69
+ return JSON.parse(body)
70
+ end
71
+
72
+ def print_all_descriptions
73
+ endpoints.each do |endpoint_name|
74
+ self.print_description(endpoint_name)
75
+ puts
76
+ end
77
+ end
78
+
79
+ def print_description(endpoint_name)
80
+ endpoint_str = "Endpoint: #{endpoint_name}"
81
+ puts "=" * endpoint_str.length
82
+ puts endpoint_str
83
+ puts "=" * endpoint_str.length
84
+
85
+ self[endpoint_name.to_sym].methods.each do |method_name|
86
+ puts
87
+ method = self[endpoint_name][method_name]
88
+ puts " Method: #{method_name.inspect}"
89
+ puts " Description: #{method.description}"
90
+ required_params = method.parameters.map { |p| p.inspect }
91
+ if required_params.any?
92
+ puts " Required parameters: #{required_params.join(', ')}"
93
+ end
94
+ end
95
+ end
96
+
97
+ end
@@ -0,0 +1,24 @@
1
+ class NationBuilder::Endpoint
2
+
3
+ def initialize(name)
4
+ @name = name
5
+ @name_to_method = {}
6
+ end
7
+
8
+ def name
9
+ @name.downcase.gsub(' ', '_').to_sym
10
+ end
11
+
12
+ def register_method(method)
13
+ @name_to_method[method.name] = method
14
+ end
15
+
16
+ def methods
17
+ @methods ||= @name_to_method.keys
18
+ end
19
+
20
+ def [](method_name)
21
+ @name_to_method[method_name]
22
+ end
23
+
24
+ end
@@ -0,0 +1,48 @@
1
+ class NationBuilder::Method
2
+
3
+ attr_reader :uri, :http_method, :description
4
+
5
+ def initialize(name, http_method, uri, description)
6
+ @name = name
7
+ @http_method = http_method.downcase.to_sym
8
+ @uri = uri
9
+ @description = description
10
+ @name_to_parameter = {}
11
+ end
12
+
13
+ def register_parameter(parameter)
14
+ @name_to_parameter[parameter.name] = parameter
15
+ end
16
+
17
+ def parameters
18
+ @name_to_parameter.keys
19
+ end
20
+
21
+ def name
22
+ @name.downcase.gsub(' ', '_').to_sym
23
+ end
24
+
25
+ def validate_args(args)
26
+ if Set.new(args.keys) != Set.new(parameters)
27
+ raise ArgumentError
28
+ .new("Required args: #{parameters.join(', ')}. Provided args: #{args.keys.join(', ')}")
29
+ end
30
+ end
31
+
32
+ def method_args(args)
33
+ a = {}
34
+ args.each do |k, v|
35
+ a[k] = v if parameters.include?(k)
36
+ end
37
+ a
38
+ end
39
+
40
+ def nonmethod_args(args)
41
+ a = {}
42
+ args.each do |k, v|
43
+ a[k] = v unless parameters.include?(k)
44
+ end
45
+ a
46
+ end
47
+
48
+ end
@@ -0,0 +1,9 @@
1
+ class NationBuilder::Parameter
2
+
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name.to_sym
7
+ end
8
+
9
+ end
@@ -0,0 +1,28 @@
1
+ class NationBuilder::SpecParser
2
+
3
+ def self.parse(spec_path)
4
+ spec = JSON.parse(File.read(spec_path))
5
+ endpoints = []
6
+
7
+ spec['endpoints'].each do |endpoint|
8
+ nb_endpoint = NationBuilder::Endpoint.new(endpoint['name'])
9
+ endpoints << nb_endpoint
10
+ endpoint['methods'].each do |method|
11
+ nb_method = NationBuilder::Method.new(method['MethodName'],
12
+ method['HTTPMethod'],
13
+ method['URI'],
14
+ method['Synopsis'])
15
+ nb_endpoint.register_method(nb_method)
16
+ method['parameters'].each do |parameter|
17
+ if (parameter['Required'] == 'Y') && (parameter['Name'] != 'body')
18
+ nb_parameter = NationBuilder::Parameter.new(parameter['Name'])
19
+ nb_method.register_parameter(nb_parameter)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ endpoints
26
+ end
27
+
28
+ end
@@ -0,0 +1,80 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: nationbuilder-rb 0.0.1 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "nationbuilder-rb"
9
+ s.version = "0.0.1"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.authors = ["David Huie"]
13
+ s.date = "2014-06-26"
14
+ s.description = "A Ruby client to the NationBuilder API"
15
+ s.email = "david@nationbuilder.com"
16
+ s.executables = ["nbdoc"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".rspec",
24
+ ".ruby-gemset",
25
+ "Gemfile",
26
+ "Gemfile.lock",
27
+ "LICENSE.txt",
28
+ "README.md",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "bin/nbdoc",
32
+ "lib/nationbuilder.rb",
33
+ "lib/nationbuilder/api_spec.json",
34
+ "lib/nationbuilder/client.rb",
35
+ "lib/nationbuilder/endpoint.rb",
36
+ "lib/nationbuilder/method.rb",
37
+ "lib/nationbuilder/parameter.rb",
38
+ "lib/nationbuilder/spec_parser.rb",
39
+ "nationbuilder-rb.gemspec",
40
+ "spec/fixtures/delete.yml",
41
+ "spec/fixtures/parametered_get.yml",
42
+ "spec/fixtures/parametered_post.yml",
43
+ "spec/fixtures/parametered_put.yml",
44
+ "spec/nationbuilder_client_spec.rb",
45
+ "spec/spec_helper.rb"
46
+ ]
47
+ s.homepage = "http://github.com/3dna/nationbuilder-rb"
48
+ s.licenses = ["MIT"]
49
+ s.require_paths = ["lib"]
50
+ s.rubygems_version = "2.1.11"
51
+ s.summary = "A Ruby client to the NationBuilder API"
52
+
53
+ if s.respond_to? :specification_version then
54
+ s.specification_version = 4
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<httpclient>, ["~> 2.4.0"])
58
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
59
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
60
+ s.add_development_dependency(%q<simplecov>, ["~> 0.8.2"])
61
+ s.add_development_dependency(%q<vcr>, ["~> 2.9.2"])
62
+ s.add_development_dependency(%q<webmock>, ["~> 1.18.0"])
63
+ else
64
+ s.add_dependency(%q<httpclient>, ["~> 2.4.0"])
65
+ s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
66
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
67
+ s.add_dependency(%q<simplecov>, ["~> 0.8.2"])
68
+ s.add_dependency(%q<vcr>, ["~> 2.9.2"])
69
+ s.add_dependency(%q<webmock>, ["~> 1.18.0"])
70
+ end
71
+ else
72
+ s.add_dependency(%q<httpclient>, ["~> 2.4.0"])
73
+ s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
74
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
75
+ s.add_dependency(%q<simplecov>, ["~> 0.8.2"])
76
+ s.add_dependency(%q<vcr>, ["~> 2.9.2"])
77
+ s.add_dependency(%q<webmock>, ["~> 1.18.0"])
78
+ end
79
+ end
80
+
@@ -0,0 +1,65 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: delete
5
+ uri: https://dh.nationbuilder.com/api/v1/people/15
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"access_token":"6e516b5d9a3d21b3cc079bc303c01aae3d59c8d4a7e9e6d2acfc11dada0e054e"}'
9
+ headers:
10
+ User-Agent:
11
+ - HTTPClient/1.0 (2.4.0, ruby 2.0.0 (2013-11-22))
12
+ Accept:
13
+ - application/json
14
+ Date:
15
+ - Wed, 25 Jun 2014 22:41:37 GMT
16
+ Content-Type:
17
+ - application/json
18
+ response:
19
+ status:
20
+ code: 204
21
+ message: No Content
22
+ headers:
23
+ Server:
24
+ - cloudflare-nginx
25
+ Date:
26
+ - Wed, 25 Jun 2014 22:41:39 GMT
27
+ Content-Type:
28
+ - application/octet-stream
29
+ Content-Length:
30
+ - '0'
31
+ Connection:
32
+ - keep-alive
33
+ Set-Cookie:
34
+ - __cfduid=d4ac3918ff3dbcc509787eadf8b8b90451403736098139; expires=Mon, 23-Dec-2019
35
+ 23:50:00 GMT; path=/; domain=.nationbuilder.com; HttpOnly
36
+ X-Ua-Compatible:
37
+ - IE=Edge,chrome=1
38
+ Cache-Control:
39
+ - no-cache
40
+ Nation-Ratelimit-Limit:
41
+ - '10000'
42
+ Nation-Ratelimit-Remaining:
43
+ - '9968'
44
+ Nation-Ratelimit-Reset:
45
+ - '1403740800'
46
+ X-Request-Id:
47
+ - f00da219be79923c08a78e821cc4897b
48
+ X-Runtime:
49
+ - '0.994571'
50
+ X-Rack-Cache:
51
+ - invalidate, pass
52
+ X-Powered-By:
53
+ - Phusion Passenger 4.0.23
54
+ Status:
55
+ - 204 No Content
56
+ Via:
57
+ - 1.1 nationbuilder.com
58
+ Cf-Ray:
59
+ - 1404ac75519c0d67-LAX
60
+ body:
61
+ encoding: UTF-8
62
+ string: ''
63
+ http_version:
64
+ recorded_at: Wed, 25 Jun 2014 22:41:39 GMT
65
+ recorded_with: VCR 2.9.2
@@ -0,0 +1,79 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://dh.nationbuilder.com/api/v1/sites/dh/pages/basic_pages?access_token=6e516b5d9a3d21b3cc079bc303c01aae3d59c8d4a7e9e6d2acfc11dada0e054e
6
+ body:
7
+ encoding: UTF-8
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - HTTPClient/1.0 (2.4.0, ruby 2.0.0 (2013-11-22))
12
+ Accept:
13
+ - application/json
14
+ Date:
15
+ - Wed, 25 Jun 2014 22:28:30 GMT
16
+ Content-Type:
17
+ - application/json
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Server:
24
+ - cloudflare-nginx
25
+ Date:
26
+ - Wed, 25 Jun 2014 22:28:31 GMT
27
+ Content-Type:
28
+ - application/json
29
+ Transfer-Encoding:
30
+ - chunked
31
+ Connection:
32
+ - keep-alive
33
+ Set-Cookie:
34
+ - __cfduid=d32a8837f08e39176e4d1ed146327e7201403735310572; expires=Mon, 23-Dec-2019
35
+ 23:50:00 GMT; path=/; domain=.nationbuilder.com; HttpOnly
36
+ X-Ua-Compatible:
37
+ - IE=Edge,chrome=1
38
+ Cache-Control:
39
+ - max-age=0, private, must-revalidate
40
+ Nation-Ratelimit-Limit:
41
+ - '10000'
42
+ Nation-Ratelimit-Remaining:
43
+ - '9973'
44
+ Nation-Ratelimit-Reset:
45
+ - '1403740800'
46
+ X-Request-Id:
47
+ - c833875bc9d08cf43ca93454d0a4f70f
48
+ X-Runtime:
49
+ - '0.697364'
50
+ X-Rack-Cache:
51
+ - miss
52
+ X-Powered-By:
53
+ - Phusion Passenger 4.0.23
54
+ Status:
55
+ - 200 OK
56
+ Vary:
57
+ - Accept-Encoding
58
+ Via:
59
+ - 1.1 nationbuilder.com
60
+ Cf-Ray:
61
+ - 1404993b14380d67-LAX
62
+ body:
63
+ encoding: UTF-8
64
+ string: '{"page":1,"total_pages":1,"per_page":10,"total":1,"results":[{"slug":"about","path":"/about","status":"published","site_slug":"dh","name":"About","headline":"About","title":"About
65
+ - dh","excerpt":"","author_id":2,"published_at":"2013-07-08T11:07:00-07:00","external_id":null,"tags":[],"id":1,"content":"<div
66
+ style=\"float: right; margin: 0 0 10px 25px;\"><img class=\"img-circle\" src=\"boats.jpg\"
67
+ alt=\"\"></div>\r\n<p>This is where you tell everyone what your blog is about.
68
+ Explain what the intended purpose of your blog is, if there is one. If multiple
69
+ people will be writing for your blog, you might consider posting photos and
70
+ bios of the contributors.</p>\r\n<p>Proin libero diam, consequat ut nisl vestibulum,
71
+ commodo scelerisque lacus. In vehicula, est quis rhoncus tristique, dolor
72
+ sapien aliquam arcu, ut vulputate quam enim quis augue. Phasellus id nunc
73
+ vitae lectus dapibus viverra malesuada at nibh. Suspendisse sagittis odio
74
+ non ipsum feugiat porta.</p>\r\n<p>\u00a0</p>\r\n<p><strong>To change this
75
+ content, click \"Edit this page\" in the Supporter Nav on the right,<br>or
76
+ from your Control Panel navigate to Websites &gt; [about] &gt; Content.</strong></p>"}]}'
77
+ http_version:
78
+ recorded_at: Wed, 25 Jun 2014 22:28:31 GMT
79
+ recorded_with: VCR 2.9.2
@@ -0,0 +1,68 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://dh.nationbuilder.com/api/v1/people
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"person":{"email":"bob@example.com","last_name":"Smith","first_name":"Bob"},"access_token":"6e516b5d9a3d21b3cc079bc303c01aae3d59c8d4a7e9e6d2acfc11dada0e054e"}'
9
+ headers:
10
+ User-Agent:
11
+ - HTTPClient/1.0 (2.4.0, ruby 2.0.0 (2013-11-22))
12
+ Accept:
13
+ - application/json
14
+ Date:
15
+ - Wed, 25 Jun 2014 22:35:22 GMT
16
+ Content-Type:
17
+ - application/json
18
+ response:
19
+ status:
20
+ code: 201
21
+ message: Created
22
+ headers:
23
+ Server:
24
+ - cloudflare-nginx
25
+ Date:
26
+ - Wed, 25 Jun 2014 22:35:22 GMT
27
+ Content-Type:
28
+ - application/json
29
+ Transfer-Encoding:
30
+ - chunked
31
+ Connection:
32
+ - keep-alive
33
+ Set-Cookie:
34
+ - __cfduid=d184a711d4f638864b5e1fa438601ebdc1403735721919; expires=Mon, 23-Dec-2019
35
+ 23:50:00 GMT; path=/; domain=.nationbuilder.com; HttpOnly
36
+ X-Ua-Compatible:
37
+ - IE=Edge,chrome=1
38
+ Cache-Control:
39
+ - max-age=0, private, must-revalidate
40
+ Nation-Ratelimit-Limit:
41
+ - '10000'
42
+ Nation-Ratelimit-Remaining:
43
+ - '9972'
44
+ Nation-Ratelimit-Reset:
45
+ - '1403740800'
46
+ X-Request-Id:
47
+ - 34e7f148285faf579d2885dee6cb5571
48
+ X-Runtime:
49
+ - '0.715257'
50
+ X-Rack-Cache:
51
+ - invalidate, pass
52
+ X-Powered-By:
53
+ - Phusion Passenger 4.0.23
54
+ Status:
55
+ - 201 Created
56
+ Vary:
57
+ - Accept-Encoding
58
+ Via:
59
+ - 1.1 nationbuilder.com
60
+ Cf-Ray:
61
+ - 1404a345f7b20d67-LAX
62
+ body:
63
+ encoding: UTF-8
64
+ string: '{"person":{"birthdate":null,"city_district":null,"civicrm_id":null,"county_district":null,"county_file_id":null,"created_at":"2014-06-25T15:35:22-07:00","do_not_call":false,"do_not_contact":false,"dw_id":null,"email":"bob@example.com","email_opt_in":true,"employer":null,"external_id":null,"federal_district":null,"fire_district":null,"first_name":"Bob","has_facebook":false,"id":15,"is_twitter_follower":false,"is_volunteer":false,"judicial_district":null,"labour_region":null,"last_name":"Smith","linkedin_id":null,"mobile":null,"mobile_opt_in":true,"nbec_guid":null,"ngp_id":null,"note":null,"occupation":null,"party":null,"pf_strat_id":null,"phone":null,"precinct_id":null,"primary_address":null,"recruiter_id":null,"rnc_id":null,"rnc_regid":null,"salesforce_id":null,"school_district":null,"school_sub_district":null,"sex":null,"state_file_id":null,"state_lower_district":null,"state_upper_district":null,"support_level":null,"supranational_district":null,"tags":[],"twitter_id":null,"twitter_name":null,"updated_at":"2014-06-25T15:35:22-07:00","van_id":null,"village_district":null,"active_customer_expires_at":null,"active_customer_started_at":null,"author":null,"author_id":null,"auto_import_id":null,"availability":null,"banned_at":null,"billing_address":null,"bio":null,"call_status_id":null,"call_status_name":null,"capital_amount_in_cents":0,"children_count":0,"church":null,"city_sub_district":null,"closed_invoices_amount_in_cents":null,"closed_invoices_count":null,"contact_status_id":null,"contact_status_name":null,"could_vote_status":null,"demo":null,"donations_amount_in_cents":0,"donations_amount_this_cycle_in_cents":0,"donations_count":0,"donations_count_this_cycle":0,"donations_pledged_amount_in_cents":0,"donations_raised_amount_in_cents":0,"donations_raised_amount_this_cycle_in_cents":0,"donations_raised_count":0,"donations_raised_count_this_cycle":0,"donations_to_raise_amount_in_cents":0,"email1":"bob@example.com","email1_is_bad":false,"email2":null,"email2_is_bad":false,"email3":null,"email3_is_bad":false,"email4":null,"email4_is_bad":false,"ethnicity":null,"facebook_address":null,"facebook_profile_url":null,"facebook_updated_at":null,"facebook_username":null,"fax_number":null,"federal_donotcall":false,"first_donated_at":null,"first_fundraised_at":null,"first_invoice_at":null,"first_prospect_at":null,"first_recruited_at":null,"first_supporter_at":"2014-06-25T15:35:22-07:00","first_volunteer_at":null,"full_name":"Bob
65
+ Smith","home_address":null,"import_id":null,"inferred_party":null,"inferred_support_level":null,"invoice_payments_amount_in_cents":0,"invoice_payments_referred_amount_in_cents":0,"invoices_amount_in_cents":null,"invoices_count":null,"is_deceased":false,"is_donor":false,"is_fundraiser":false,"is_ignore_donation_limits":false,"is_leaderboardable":true,"is_mobile_bad":false,"is_possible_duplicate":false,"is_profile_private":false,"is_profile_searchable":true,"is_prospect":false,"is_supporter":true,"is_survey_question_private":false,"language":null,"last_call_id":null,"last_contacted_at":null,"last_contacted_by":null,"last_donated_at":null,"last_fundraised_at":null,"last_invoice_at":null,"last_rule_violation_at":null,"legal_name":null,"locale":null,"mailing_address":null,"marital_status":null,"media_market_name":null,"meetup_address":null,"membership_expires_at":null,"membership_level_name":null,"membership_started_at":null,"middle_name":null,"mobile_normalized":null,"nbec_precinct_code":null,"note_updated_at":null,"outstanding_invoices_amount_in_cents":null,"outstanding_invoices_count":null,"overdue_invoices_count":0,"page_slug":null,"parent":null,"parent_id":null,"party_member":false,"phone_normalized":null,"phone_time":null,"precinct_code":null,"precinct_name":null,"prefix":null,"previous_party":null,"primary_email_id":1,"priority_level":null,"priority_level_changed_at":null,"profile_content":null,"profile_content_html":null,"profile_headline":null,"received_capital_amount_in_cents":0,"recruiter":null,"recruits_count":0,"registered_address":null,"registered_at":null,"religion":null,"rule_violations_count":0,"spent_capital_amount_in_cents":0,"submitted_address":null,"subnations":[],"suffix":null,"support_level_changed_at":null,"support_probability_score":null,"turnout_probability_score":null,"twitter_address":null,"twitter_description":null,"twitter_followers_count":null,"twitter_friends_count":null,"twitter_location":null,"twitter_login":null,"twitter_updated_at":null,"twitter_website":null,"unsubscribed_at":null,"user_submitted_address":null,"username":null,"warnings_count":0,"website":null,"work_address":null,"work_phone_number":null},"precinct":null}'
66
+ http_version:
67
+ recorded_at: Wed, 25 Jun 2014 22:35:23 GMT
68
+ recorded_with: VCR 2.9.2