change-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ # $LOAD_PATH << './lib' # Uncomment to load change-ruby in console without installing gem.
2
+
3
+ require 'digest/sha2'
4
+ require 'httparty'
5
+
6
+ require 'exceptions/change_exception'
7
+
8
+ require 'requests/const'
9
+ require 'requests/client'
10
+
11
+ require 'resources/resource'
12
+ require 'resources/collection_resource'
13
+ require 'resources/member_resource'
14
+ require 'resources/organization'
15
+ require 'resources/user'
16
+ require 'resources/petition'
17
+ require 'resources/petition_collection'
18
+ require 'resources/signature_collection'
19
+ require 'resources/target_collection'
20
+ require 'resources/reason_collection'
21
+ require 'resources/update_collection'
@@ -0,0 +1,18 @@
1
+ module Change
2
+ module Exceptions
3
+ class ChangeException < StandardError
4
+
5
+ attr_reader :code
6
+ attr_reader :messages
7
+
8
+ def initialize(messages, code = nil)
9
+ @code = code
10
+ @messages = messages
11
+ end
12
+
13
+ def message
14
+ @messages.first
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,71 @@
1
+ module Change
2
+ module Requests
3
+ class Client
4
+ include HTTParty
5
+ include Change::Exceptions
6
+
7
+ def initialize(properties = {})
8
+ @api_key = properties.delete(:api_key)
9
+ @secret_token = properties.delete(:secret_token)
10
+ raise "An API key must be specified." if @api_key.nil?
11
+ end
12
+
13
+ def request(request_on, request_type, resource, params)
14
+ method = request_type.delete(:method)
15
+ action_or_collection = request_type.delete(:action) || request_type.delete(:collection)
16
+ endpoint = resource.endpoint(request_on, action_or_collection)
17
+
18
+ params[:api_key] = @api_key
19
+
20
+ if resource.needs_authorization?(method)
21
+ params[:endpoint] = endpoint
22
+ params[:timestamp] = Time.now.utc.iso8601
23
+
24
+ auth_key_to_use = params.delete(:auth_key_to_use)
25
+ params[:rsig] = generate_rsig(params, auth_key_to_use['auth_key'])
26
+ end
27
+
28
+ response = send(method.to_s, final_url(endpoint), params)
29
+ deal_with_response(response)
30
+ end
31
+
32
+ private
33
+
34
+ def deal_with_response(response)
35
+ case response.code
36
+ when 200, 202
37
+ response.parsed_response
38
+ else
39
+ messages = response.parsed_response['messages']
40
+ raise ChangeException.new(messages, response.code), messages.join(' '), caller
41
+ end
42
+ end
43
+
44
+ def base_url
45
+ "https://#{Change::HOST}"
46
+ end
47
+
48
+ def final_url(endpoint)
49
+ base_url + endpoint
50
+ end
51
+
52
+ def get(url, params)
53
+ HTTParty.get(url, { :query => params })
54
+ end
55
+
56
+ def post(url, params)
57
+ HTTParty.post(url, { :body => params })
58
+ end
59
+
60
+ def generate_rsig(params, auth_key)
61
+ body_to_digest = "#{post_body(params)}#{@secret_token}#{auth_key}"
62
+ Digest::SHA2.hexdigest(body_to_digest)
63
+ end
64
+
65
+ def post_body(params)
66
+ HTTParty::HashConversions.to_params(params)
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,4 @@
1
+ module Change
2
+ HOST = 'api.change.org'
3
+ VERSION = 'v1'
4
+ end
@@ -0,0 +1,31 @@
1
+ module Change
2
+ module Resources
3
+ class CollectionResource < Resource
4
+
5
+ class << self
6
+
7
+ # Overridden for special pluralizations
8
+ def collection_name
9
+ name = self.name.split('::').last.downcase
10
+ name = name.match(/(.+)collection/)[1]
11
+ "#{name}s"
12
+ end
13
+
14
+ end
15
+
16
+ attr_accessor :parent_resource
17
+ attr_accessor :collection
18
+
19
+ def initialize(parent_resource, collection = nil, properties = {})
20
+ @parent_resource = parent_resource
21
+ @collection = collection unless collection.nil?
22
+ super(@parent_resource.client, properties)
23
+ end
24
+
25
+ def load(params)
26
+ @parent_resource.load_collection(self.class.collection_name.to_sym, params)
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,73 @@
1
+ module Change
2
+ module Resources
3
+ class MemberResource < Resource
4
+
5
+ class << self
6
+
7
+ # This is the Change.org name for the resource type. It
8
+ # is automatically derived from the class name.
9
+ #
10
+ # @return [String] the name of the resource
11
+ def member_name
12
+ self.name.split('::').last.downcase
13
+ end
14
+
15
+ # This is the Change.org pluralized name for the resource type. While it
16
+ # can be overridden in sub-classes for non-standard English
17
+ # pluralizations, it is automatically derived from the class name.
18
+ #
19
+ # @return [String] the pluralized name of the resource
20
+ def collection_name
21
+ "#{self.name.split('::').last.downcase}s"
22
+ end
23
+
24
+ end
25
+
26
+ # The unique Change.org ID of the resource.
27
+ attr_accessor :id
28
+
29
+ # The fields on the resource. The Change.org API documentation has the
30
+ # full list of fields that may be returned for each resource.
31
+ attr_accessor :properties
32
+
33
+ def initialize(client, properties = {})
34
+ @id = properties.delete(:id) || properties.delete("#{self.class.member_name}_id")
35
+ super(client, properties)
36
+ end
37
+
38
+ # Shared resource requests
39
+
40
+ # Retrieves the unique Change.org ID for the current resource by its
41
+ # resource current URL.
42
+ #
43
+ # @param resource_url [String] the current Change.org URL of the resource
44
+ # @return [Integer] the unique Change.org ID of the resource
45
+ def get_id(resource_url)
46
+ response = make_request(:collection, { :method => :get, :action => :get_id }, { "#{self.class.member_name}_url".to_sym => resource_url })
47
+ response["#{self.class.member_name}_id"]
48
+ end
49
+
50
+ def load(resource_id_or_url = nil, params = {})
51
+ if resource_id_or_url.is_a?(Integer)
52
+ @id = resource_id_or_url
53
+ elsif resource_id_or_url.is_a?(String)
54
+ @id = get_id(resource_id_or_url)
55
+ end
56
+ raise "Missing resource ID." if @id.nil?
57
+ response = make_request(:member, { :method => :get }, params)
58
+ response.delete("#{self.class.member_name}_id")
59
+ @properties = response
60
+ end
61
+
62
+ def load_collection(collection, params = {})
63
+ response = make_request(:member, { :method => :get, :collection => collection }, params)
64
+ if response.is_a?(Array)
65
+ self.send(collection).collection = response
66
+ else
67
+ self.send(collection).collection = response[collection.to_s]
68
+ end
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,13 @@
1
+ module Change
2
+ module Resources
3
+ class Organization < MemberResource
4
+
5
+ attr_accessor :petitions
6
+
7
+ def initialize(client, properties = {})
8
+ super(client, properties)
9
+ @petitions = PetitionCollection.new(self)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Change
2
+ module Resources
3
+ class Petition < MemberResource
4
+
5
+ attr_accessor :signatures
6
+ attr_accessor :targets
7
+ attr_accessor :reasons
8
+ attr_accessor :updates
9
+
10
+ def initialize(client, properties = {})
11
+ super(client, properties)
12
+ @signatures = SignatureCollection.new(self)
13
+ @targets = TargetCollection.new(self)
14
+ @reasons = ReasonCollection.new(self)
15
+ @updates = UpdateCollection.new(self)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module Change
2
+ module Resources
3
+ class PetitionCollection < CollectionResource; end
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Change
2
+ module Resources
3
+ class ReasonCollection < CollectionResource; end
4
+ end
5
+ end
@@ -0,0 +1,83 @@
1
+ module Change
2
+ module Resources
3
+ class Resource
4
+
5
+ attr_accessor :client
6
+
7
+ def initialize(client, properties = {})
8
+ @client = client
9
+ @auth_keys = []
10
+
11
+ initial_auth_key = properties.delete(:auth_key)
12
+ add_new_auth_key(initial_auth_key) unless initial_auth_key.nil?
13
+ @properties = properties
14
+ end
15
+
16
+ def make_request(request_on, request_type, params = {})
17
+ @client.request(request_on, request_type, self, params)
18
+ end
19
+
20
+ def endpoint(request_on, action_or_collection = nil)
21
+ path_parts = send("#{request_on.to_s}_path", action_or_collection)
22
+ path = path_parts.join('/')
23
+ path.prepend('/')
24
+ end
25
+
26
+ def needs_authorization?(method)
27
+ method != :get
28
+ end
29
+
30
+ def auth_key(key_number = 0)
31
+ @auth_keys[key_number]
32
+ end
33
+
34
+ def auth_key=(new_key)
35
+ add_new_auth_key(new_key)
36
+ end
37
+
38
+ # Shared resource requests
39
+
40
+ def request_auth_key(params)
41
+ response = make_request(:member, { :method => :post, :collection => :auth_keys }, params)
42
+ if response['status'] == 'granted'
43
+ add_new_auth_key(response)
44
+ true
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def add_new_auth_key(key_object)
53
+ key_object = { :auth_key => key_object } if key_object.is_a?(String)
54
+ key_object.delete('status')
55
+ key_object.delete('result')
56
+ key_object.delete("#{self.class.member_name}_id")
57
+ @auth_keys << key_object unless @auth_keys.include?(key_object)
58
+ end
59
+
60
+ def member_path(action_or_collection = nil)
61
+ raise "Can't generate a member path without an ID." if @id.nil?
62
+ path_parts = collection_path
63
+ path_parts << @id
64
+ path_parts << action_or_collection if action_or_collection
65
+ path_parts
66
+ end
67
+
68
+ def collection_path(action = nil)
69
+ path_parts = [Change::VERSION]
70
+ if @parent_resource
71
+ path_parts << @parent_resource.class.collection_name
72
+ path_parts << @parent_resource.id
73
+ path_parts << self.class.collection_name
74
+ else
75
+ path_parts << self.class.collection_name
76
+ path_parts << action if action
77
+ end
78
+ path_parts
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,19 @@
1
+ module Change
2
+ module Resources
3
+ class SignatureCollection < CollectionResource
4
+
5
+ def auth_key
6
+ @parent_resource.auth_key
7
+ end
8
+
9
+ def add_signature(params = {}, auth_key_to_use = nil)
10
+ auth_key_to_use ||= auth_key
11
+ raise "No auth key specified." if auth_key_to_use.nil?
12
+ params[:auth_key_to_use] = auth_key_to_use
13
+ params[:source] = auth_key_to_use['source']
14
+ response = make_request(:collection, { :method => :post }, params)
15
+ response['result'] == 'success'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module Change
2
+ module Resources
3
+ class TargetCollection < CollectionResource; end
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Change
2
+ module Resources
3
+ class UpdateCollection < CollectionResource; end
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ module Change
2
+ module Resources
3
+ class User < MemberResource
4
+
5
+ attr_accessor :petitions
6
+ attr_accessor :signatures_on_petitions
7
+
8
+ def initialize(client, properties = {})
9
+ super(client, properties)
10
+ @petitions = PetitionCollection.new(self)
11
+ @signatures_on_petitions = PetitionCollection.new(self)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,79 @@
1
+ require 'util/test_helper'
2
+
3
+ describe 'Client' do
4
+
5
+ MockResponse = Struct.new("Response", :code, :parsed_response)
6
+
7
+ before do
8
+ @api_key_example = 'goodbye_world'
9
+ @secret_token_example = 'hello_world'
10
+ @client = Change::Requests::Client.new({
11
+ :api_key => @api_key_example,
12
+ :secret_token => @secret_token_example
13
+ })
14
+ @petition = Change::Resources::Petition.new(@client)
15
+ end
16
+
17
+ describe '#request' do
18
+
19
+ it "should load a resource with a get request" do
20
+ url_to_be_called = 'https://api.change.org/v1/petitions/1'
21
+ params = { :api_key => @api_key_example }
22
+ @client.expects(:send)
23
+ .with('get', url_to_be_called, params)
24
+ .once
25
+ .returns(MockResponse.new(200, { "name" => "hey world" }))
26
+ @petition.load(1)
27
+ @petition.properties['name'].must_equal "hey world"
28
+ end
29
+
30
+ it "should modify a resource with a post request" do
31
+ @petition.id = 2
32
+ url_to_be_called = 'https://api.change.org/v1/petitions/2/signatures'
33
+ params = {
34
+ :api_key => @api_key_example,
35
+ :first_name => 'Jean-Luc',
36
+ :last_name => 'Picard',
37
+ :city => 'Marseille',
38
+ :postal_code => '13055',
39
+ :country_code => 'FR',
40
+ :email => 'jlp@enterprise1701d.com'
41
+ }
42
+ @client.expects(:send)
43
+ .with('post', url_to_be_called, params)
44
+ .once
45
+ .returns(MockResponse.new(200, { "result" => "success" }))
46
+ @petition.signatures.add_signature(params, { 'auth_key' => 'my_on_call_auth_key', 'source' => 'my_source' }).must_equal true
47
+ end
48
+
49
+ it "should raise an exception if the response indicates the request was not a successful" do
50
+ url_to_be_called = 'https://api.change.org/v1/petitions/1'
51
+ params = { :api_key => @api_key_example }
52
+ @client.expects(:send)
53
+ .with('get', url_to_be_called, params)
54
+ .once
55
+ .returns(MockResponse.new(400, { "result" => "failure", "messages" => [ "bad thing one", "bad thing two"] }))
56
+ lambda { @petition.load(1) }.must_raise(Change::Exceptions::ChangeException)
57
+ end
58
+
59
+ end
60
+
61
+ describe '#final_url' do
62
+
63
+ it "should return the proper URL with the host and protocol" do
64
+ @client.send(:final_url, '/v1/something').must_equal 'https://api.change.org/v1/something'
65
+ end
66
+
67
+ end
68
+
69
+ describe '#generate_rsig' do
70
+
71
+ it "should properly generate the request signature for the given body" do
72
+ @petition.auth_key = { 'auth_key' => 'my_on_call_auth_key', 'source' => 'my_source' }
73
+ params = { "first_arg" => "whatever is in the body" }
74
+ to_digest = HTTParty::HashConversions.to_params(params) + @secret_token_example + @petition.auth_key['auth_key']
75
+ expected_rsig = Digest::SHA2.hexdigest(to_digest)
76
+ @client.send(:generate_rsig, params, @petition.auth_key['auth_key']).must_equal expected_rsig
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,60 @@
1
+ require 'util/test_helper'
2
+
3
+ class TesterCollection < Change::Resources::CollectionResource; end
4
+ class TesterMember < Change::Resources::MemberResource
5
+ attr_accessor :testers
6
+
7
+ def initialize(client, properties = {})
8
+ super(client, properties)
9
+ @testers = TesterCollection.new(self)
10
+ end
11
+
12
+ end
13
+
14
+ describe 'CollectionResource' do
15
+
16
+ describe '#collection_name' do
17
+
18
+ it "should derive the collection name from the class name before 'Collection'" do
19
+ correct_collection_name = "testers"
20
+ TesterCollection.collection_name.must_equal correct_collection_name
21
+ end
22
+
23
+ end
24
+
25
+ describe '#initialize' do
26
+
27
+ it "should initialize the resource with the specified parent resource and set the client to that of the parent" do
28
+ auth_key = { :auth_key => 'a test' }
29
+ client = mock
30
+ parent_resource = TesterMember.new(client)
31
+ resource = TesterCollection.new(parent_resource)
32
+ resource.parent_resource.must_equal parent_resource
33
+ resource.client.must_equal client
34
+ end
35
+
36
+ end
37
+
38
+ describe 'after initialization' do
39
+
40
+ before do
41
+ @client = mock
42
+ @parent_resource = TesterMember.new(@client)
43
+ @resource = TesterCollection.new(@parent_resource)
44
+ end
45
+
46
+ describe '#load' do
47
+
48
+ it "should load the collection using the parent resource" do
49
+ params = { :param1 => 1}
50
+ returned_collection = [1, 2]
51
+ collection_name = TesterCollection.collection_name.to_sym
52
+ @parent_resource.expects(:load_collection).with(collection_name, params).once
53
+ @resource.load(params)
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,208 @@
1
+ require 'util/test_helper'
2
+
3
+ class WidgetsCollection < Change::Resources::CollectionResource; end
4
+ class TesterResource < Change::Resources::MemberResource
5
+
6
+ attr_accessor :widgets
7
+
8
+ def initialize(client, properties = {})
9
+ super(client, properties)
10
+ @widgets = WidgetsCollection.new(self)
11
+ end
12
+ end
13
+
14
+ describe 'MemberResource' do
15
+
16
+ describe '#initialize' do
17
+
18
+ it "should initialize the resource with id, properties, and an auth key if specified" do
19
+ auth_key = { :auth_key => 'a test' }
20
+ properties = { :id => 3, :auth_key => auth_key, :other => 'what' }
21
+ resource_with_auth_key = TesterResource.new(@client, properties)
22
+ resource_with_auth_key.auth_key.must_equal auth_key
23
+ resource_with_auth_key.id.must_equal 3
24
+ resource_with_auth_key.properties[:other].must_equal 'what'
25
+ end
26
+
27
+ end
28
+
29
+ describe 'after initialization' do
30
+
31
+ before do
32
+ @client = mock
33
+ @resource = TesterResource.new(@client)
34
+ end
35
+
36
+ describe '#needs_authorization?' do
37
+
38
+ it "should return true for :get" do
39
+ @resource.needs_authorization?(:get).must_equal false
40
+ end
41
+
42
+ it "should return false for :post, :put, and :delete" do
43
+ @resource.needs_authorization?(:post).must_equal true
44
+ @resource.needs_authorization?(:put).must_equal true
45
+ @resource.needs_authorization?(:delete).must_equal true
46
+ end
47
+
48
+ end
49
+
50
+ describe '#auth_key' do
51
+
52
+ before do
53
+ @test_key_1 = { :auth_key => 'test_key', :source => 'test source' }
54
+ @test_key_2 = { :auth_key => 'test_key2', :source => 'test source 2' }
55
+ end
56
+
57
+ it "should add a new auth key to the resource and return the first one or a specific one" do
58
+ @resource.auth_key = @test_key_1
59
+ @resource.auth_key.must_equal @test_key_1
60
+
61
+ @resource.auth_key = @test_key_2
62
+ @resource.auth_key.must_equal @test_key_1
63
+ @resource.auth_key(1).must_equal @test_key_2
64
+ end
65
+
66
+ end
67
+
68
+ describe '#make_request' do
69
+
70
+ before do
71
+ @resource.id = 1
72
+ @request_on = :member
73
+ @request_type = { :method => :get }
74
+ @params = {}
75
+ end
76
+
77
+ it "should make a call to its client's request method" do
78
+ @client.expects(:request).with(@request_on, @request_type, @resource, @params).once
79
+ @resource.make_request(@request_on, @request_type, @params)
80
+ end
81
+
82
+ end
83
+
84
+ describe '#endpoint' do
85
+
86
+ before do
87
+ @resource.id = 2
88
+ end
89
+
90
+ it "should return a proper endpoint path for a specific member" do
91
+ @resource.endpoint(:member).must_equal '/v1/testerresources/2'
92
+ end
93
+
94
+ it "should return a proper endpoint path for a sub-collection of a specific member" do
95
+ @resource.endpoint(:member, :petitions).must_equal '/v1/testerresources/2/petitions'
96
+ end
97
+
98
+ it "should return a proper endpoint path for resource collection" do
99
+ @resource.endpoint(:collection).must_equal '/v1/testerresources'
100
+ end
101
+ end
102
+
103
+ describe '#request_auth_key' do
104
+
105
+ it "should return true if an auth key is granted" do
106
+ @resource.expects(:make_request)
107
+ .with(:member, { :method => :post, :collection => :auth_keys }, {})
108
+ .returns({ 'status' => 'granted', 'api_key' => "test" })
109
+ @resource.request_auth_key({}).must_equal true
110
+ end
111
+
112
+ it "should return false if an auth key is not granted" do
113
+ @resource.expects(:make_request)
114
+ .with(:member, { :method => :post, :collection => :auth_keys }, {})
115
+ .returns({ 'status' => 'denied', 'api_key' => "test" })
116
+ @resource.request_auth_key({}).must_equal false
117
+ end
118
+
119
+ end
120
+
121
+ describe '#get_id' do
122
+
123
+ it "should make a request to get the ID of the resource by URL" do
124
+ resource_url = "http://www.change.org/petitions/i-am-a-url"
125
+ @resource.expects(:make_request)
126
+ .with(:collection, { :method => :get, :action => :get_id }, { "#{TesterResource.member_name}_url".to_sym => resource_url })
127
+ .returns({ "#{TesterResource.member_name}_id" => 1 })
128
+ @resource.get_id(resource_url).must_equal 1
129
+ end
130
+
131
+ end
132
+
133
+ end
134
+
135
+ describe '#load' do
136
+
137
+ before do
138
+ @client = mock
139
+ @resource = TesterResource.new(@client)
140
+ end
141
+
142
+ it "should load the resource by ID" do
143
+ res_id = 3
144
+ params = {}
145
+ @resource.expects(:get_id).never
146
+ @resource.expects(:make_request)
147
+ .with(:member, { :method => :get }, params)
148
+ .returns({
149
+ 'name' => "John",
150
+ 'age' => 43
151
+ })
152
+ @resource.load(res_id)
153
+ @resource.id.must_equal 3
154
+ @resource.properties['name'].must_equal "John"
155
+ @resource.properties['age'].must_equal 43
156
+ end
157
+
158
+ it "should load the resource by URL" do
159
+ resource_url = 'http://www.change.org/petitions/example'
160
+ params = {}
161
+ @resource.expects(:get_id).with(resource_url).returns(93)
162
+ @resource.expects(:make_request)
163
+ .with(:member, { :method => :get }, params)
164
+ .returns({
165
+ 'name' => "Billy Jo",
166
+ 'age' => 8
167
+ })
168
+ @resource.load(resource_url)
169
+ @resource.id.must_equal 93
170
+ @resource.properties['name'].must_equal "Billy Jo"
171
+ @resource.properties['age'].must_equal 8
172
+ end
173
+
174
+ end
175
+
176
+ describe '#load_collection' do
177
+
178
+ before do
179
+ @client = mock
180
+ @resource = TesterResource.new(@client)
181
+ @params = {}
182
+ @returned_widgets = [1, 3, 5, 7]
183
+ end
184
+
185
+ it "should load collection by dumping the returned array if that's what is returned" do
186
+ @resource.widgets.collection.must_be_nil
187
+ @resource.expects(:make_request)
188
+ .with(:member, { :method => :get, :collection => :widgets }, @params)
189
+ .returns(@returned_widgets)
190
+ @resource.load_collection(:widgets)
191
+ @resource.widgets.collection.must_equal @returned_widgets
192
+ end
193
+
194
+ it "should load collection by getting the matching object attribute and dumping that array" do
195
+ @resource.widgets.collection.must_be_nil
196
+ @resource.expects(:make_request)
197
+ .with(:member, { :method => :get, :collection => :widgets }, @params)
198
+ .returns({
199
+ 'an_attr' => 'something',
200
+ 'name' => 'hello',
201
+ 'widgets' => @returned_widgets
202
+ })
203
+ @resource.load_collection(:widgets)
204
+ @resource.widgets.collection.must_equal @returned_widgets
205
+ end
206
+
207
+ end
208
+ end
@@ -0,0 +1,83 @@
1
+ require 'util/test_helper'
2
+
3
+ describe 'SignatureCollection' do
4
+
5
+ describe '#auth_key' do
6
+
7
+ before do
8
+ client = mock
9
+ @test_key = 'my_test_key'
10
+ @parent_resource = Change::Resources::Petition.new(client, { :auth_key => { 'auth_key' => @test_key } })
11
+ @signature_collection = Change::Resources::SignatureCollection.new(@parent_resource)
12
+ end
13
+
14
+ it "should return the parent resource's auth key" do
15
+ @signature_collection.auth_key['auth_key'].must_equal @test_key
16
+ end
17
+
18
+ end
19
+
20
+ describe '#add_signature' do
21
+
22
+ before do
23
+ @client = mock
24
+ @params = {
25
+ :first_name => 'Jean-Luc',
26
+ :last_name => 'Picard',
27
+ :city => 'Marseille',
28
+ :postal_code => '13055',
29
+ :country_code => 'FR',
30
+ :email => 'jlp@enterprise1701d.com'
31
+ }
32
+ end
33
+
34
+ describe "when there is already an auth key on the parent resource" do
35
+
36
+ before do
37
+ @test_key = 'my_test_key'
38
+ @parent_resource = Change::Resources::Petition.new(@client, { :auth_key => { 'auth_key' => @test_key } })
39
+ @signature_collection = Change::Resources::SignatureCollection.new(@parent_resource)
40
+ end
41
+
42
+ it "should post a signature to its parent petition and return true if successful" do
43
+ @signature_collection.expects(:make_request)
44
+ .with(:collection, { :method => :post }, @params)
45
+ .returns({ 'result' => 'success' })
46
+ @signature_collection.add_signature(@params).must_equal true
47
+ end
48
+
49
+ it "should post a signature to its parent petition and return false if unsuccessful" do
50
+ @signature_collection.expects(:make_request)
51
+ .with(:collection, { :method => :post }, @params)
52
+ .returns({ 'result' => 'failure' })
53
+ @signature_collection.add_signature(@params).must_equal false
54
+ end
55
+
56
+ end
57
+
58
+ describe "when there is no auth key on the parent resource" do
59
+
60
+ before do
61
+ @parent_resource = Change::Resources::Petition.new(@client)
62
+ @signature_collection = Change::Resources::SignatureCollection.new(@parent_resource)
63
+ end
64
+
65
+ it "should raise an error when trying to post a signature" do
66
+ @signature_collection.expects(:make_request).never
67
+ lambda { @signature_collection.add_signature(@params) }.must_raise(RuntimeError)
68
+ end
69
+
70
+ it "should succeed in adding a signature if an auth key is specified during the call" do
71
+ on_call_auth_key = {
72
+ 'auth_key' => 'my_on_call_auth_key',
73
+ 'source' => 'my_source'
74
+ }
75
+ @signature_collection.expects(:make_request)
76
+ .with(:collection, { :method => :post }, @params)
77
+ .returns({ 'result' => 'success' })
78
+ @signature_collection.add_signature(@params, on_call_auth_key).must_equal true
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,3 @@
1
+ require 'change-ruby'
2
+ require 'minitest/autorun'
3
+ require 'mocha/setup'
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: change-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Eric Lukoff
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.10.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.10.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mocha
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A Ruby library for the Change.org API.
63
+ email: eric@ericlukoff.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - lib/change-ruby.rb
69
+ - lib/exceptions/change_exception.rb
70
+ - lib/requests/client.rb
71
+ - lib/requests/const.rb
72
+ - lib/resources/collection_resource.rb
73
+ - lib/resources/member_resource.rb
74
+ - lib/resources/organization.rb
75
+ - lib/resources/petition.rb
76
+ - lib/resources/petition_collection.rb
77
+ - lib/resources/reason_collection.rb
78
+ - lib/resources/resource.rb
79
+ - lib/resources/signature_collection.rb
80
+ - lib/resources/target_collection.rb
81
+ - lib/resources/update_collection.rb
82
+ - lib/resources/user.rb
83
+ - test/client_test.rb
84
+ - test/collection_resource_test.rb
85
+ - test/member_resource_test.rb
86
+ - test/signature_collection_resource_test.rb
87
+ - test/util/test_helper.rb
88
+ homepage: http://rubygems.org/gems/change-ruby
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.23
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Change.org API Ruby Library (unofficial)
112
+ test_files: []
113
+ has_rdoc: