change-ruby 1.0.0

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.
@@ -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: