agree2 0.1.1

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,76 @@
1
+ require 'json'
2
+ module Agree2
3
+ class ProxyCollection
4
+ alias_method :proxy_respond_to?, :respond_to? #:nodoc:
5
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_)/ }
6
+ attr_accessor :user,:path,:singular
7
+ def initialize(container,path,class_name=nil,values=nil)
8
+ @container=container
9
+ @user=(container.is_a?(User) ? container : container.user)
10
+ @path=path
11
+ path=~/(\w+)$/
12
+ @singular=(class_name||$1.singularize).downcase.classify
13
+ @klass=eval "Agree2::#{@singular}"
14
+ if values
15
+ @target=instantiate_items(values)
16
+ else
17
+ reset
18
+ end
19
+ end
20
+
21
+ # Builds an instance of the record
22
+ def build(attributes={})
23
+ @klass.new @container,attributes
24
+ end
25
+
26
+ # Builds and saves and instance of the record
27
+ def create(attributes={})
28
+ build(attributes).save
29
+ end
30
+
31
+ # Finds the instance given the id
32
+ def find(id)
33
+ @klass.get @container,id
34
+ end
35
+
36
+ def respond_to?(symbol, include_priv = false) #:nodoc:
37
+ proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
38
+ end
39
+
40
+ # Explicitly proxy === because the instance method removal above
41
+ # doesn't catch it.
42
+ def ===(other) #:nodoc:
43
+ load_target
44
+ other === @target
45
+ end
46
+
47
+ protected
48
+
49
+ def parse_json(json) #:nodoc:
50
+ instantiate_items(JSON.parse(json))
51
+ end
52
+
53
+ def instantiate_items(list=[]) #:nodoc:
54
+ list.collect{|e|instantiate_item(e)}
55
+ end
56
+
57
+ def instantiate_item(attributes={}) #:nodoc:
58
+ @klass.new @container,attributes
59
+ end
60
+
61
+ def load_target #:nodoc:
62
+ @target||=parse_json(@user.get("#{self.path}"))
63
+ end
64
+
65
+ def reset #:nodoc:
66
+ @target=nil
67
+ end
68
+
69
+ def method_missing(method, *args, &block) #:nodoc:
70
+ if load_target
71
+ @target.send(method, *args, &block)
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,100 @@
1
+ module Agree2
2
+ # The Template allows you to create agreements based on a template. You can pick between existing community created templates or
3
+ # create your own that fits your application.
4
+ #
5
+ # To review some of the public templates available go to: https://agree2.com/masters/public
6
+ #
7
+ # If you plan on creating and signing an agreement for your user to accept the process is like this:
8
+ #
9
+ # 1. Load your template
10
+ # 2. Prepare and Sign the agreement, which fills out the template, invites your user and signs it on your behalf
11
+ # 3. Redirect the User to Accept
12
+ # 4. User Accepts agreement
13
+ # 5. Agree2 optional calls an "endpoint" web service on your web service.
14
+ # 6. User is redirected back to your application to a URL you designate
15
+ #
16
+ class Template<Agreement
17
+
18
+ def self.collection_name #:nodoc:
19
+ "masters"
20
+ end
21
+
22
+ # Prepares a template and optionally invites parties.
23
+ #
24
+ # === optional fields
25
+ #
26
+ # <tt>fields</tt> - a hash of parameters to be used to fill out the template. eg:
27
+ #
28
+ # {:amount=>100,:valid_to=>1.month.from_now,:service=>'Ruby Developer'}
29
+ #
30
+ # <tt>parties</tt> - a hash of parties. The hash should follow this format:
31
+ #
32
+ # { :client=>{:first_name=>'Bob',:last_name=>'Bryson',:email=>'bob@bob.inv',:organization_name=>'Big Client Inc'}}
33
+ #
34
+ # The above adds a party with the role *client*. The first_name, last_name and email fields are all required.
35
+ #
36
+ # <tt>application_role</tt> if you would like to add your applications user as a party set this to the role that you want:
37
+ #
38
+ # @template.prepare {},{},"broker"
39
+ #
40
+ # Adds your application as a party with the role broker.
41
+ #
42
+ # A typical example for a consulting agreement:
43
+ #
44
+ # @template.prepare {:rate=>"140",:valid_to=>"1 month from today"},
45
+ # {
46
+ # :consultant=>{:first_name=>'Alice',:last_name=>'Springs',:email=>'alice@example.com'},
47
+ # :client=>{:first_name=>'Bob',:last_name=>'Bryson',:email=>'bob@bob.inv',
48
+ # :organization_name=>'Big Client Inc'}
49
+ # }
50
+ # }
51
+ #
52
+ def prepare(fields={},parties={},application_role=nil)
53
+ Party.validate_parties_hash(parties)
54
+ parties[application_role]=:application if application_role
55
+ raw_prepare(:fields=>fields,:parties=>parties)
56
+ end
57
+
58
+ # Prepares a template, invites parties and signs it with your application
59
+ #
60
+ # === required fields
61
+ #
62
+ # <tt>fields</tt> - a hash of parameters to be used to fill out the template. eg:
63
+ #
64
+ # {:amount=>100,:valid_to=>1.month.from_now,:service=>'Ruby Developer'}
65
+ #
66
+ # <tt>parties</tt> - a hash of parties. The hash should follow this format:
67
+ #
68
+ # { :client=>{:first_name=>'Bob',:last_name=>'Bryson',:email=>'bob@bob.inv',:organization_name=>'Big Client Inc'}}
69
+ #
70
+ # The above adds a party with the role *client*. The first_name, last_name and email fields are all required.
71
+ #
72
+ # <tt>application_role</tt> if you would like to add your applications user as a party set this to the role that you want:
73
+ #
74
+ # @template.prepare_and_sign {},{},"broker"
75
+ #
76
+ # Adds your application as a party with the role broker.
77
+ #
78
+ # A typical example for a user agreement:
79
+ #
80
+ # @template.prepare_and_sign {:name=>"Alice Springs"},
81
+ # {
82
+ # :user=>{:first_name=>'Alice',:last_name=>'Springs',:email=>'alice@example.com'}
83
+ # },'service'
84
+ # }
85
+ #
86
+
87
+ def prepare_and_sign(fields={},parties={},application_role='application')
88
+ raise ArgumentError,"You need to add at least one party" if parties.empty?
89
+ Party.validate_parties_hash(parties)
90
+ parties[application_role]=:application
91
+ raw_prepare(:fields=>fields,:parties=>parties,:sign=>application_role)
92
+ end
93
+
94
+ protected
95
+
96
+ def raw_prepare(params={}) #:nodoc:
97
+ Agreement.new @user, user.post("/masters/#{permalink}/prepare",params)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,73 @@
1
+ AGREE2_JSON_HEADERS={'Content-Type'=>'application/json','Accept'=>'application/json'}
2
+ module Agree2
3
+ class User
4
+ attr_accessor :client,:access_token
5
+ def initialize(client,key,secret)
6
+ @client=client
7
+ @access_token=OAuth::AccessToken.new @client.consumer,key,secret
8
+ end
9
+
10
+ def agreements
11
+ @agreements||=Agree2::ProxyCollection.new self,'/agreements','Agreement'
12
+ end
13
+
14
+ def templates
15
+ @templates||=Agree2::ProxyCollection.new self,'/masters','Template'
16
+ end
17
+
18
+ def get(path)
19
+ handle_response @access_token.get(path,AGREE2_JSON_HEADERS)
20
+ end
21
+
22
+ def head(path)
23
+ handle_response @access_token.head(path,AGREE2_JSON_HEADERS)
24
+ end
25
+
26
+ def post(path,data=nil)
27
+ handle_response @access_token.post(path,(data ? data.to_json : nil),AGREE2_JSON_HEADERS)
28
+ end
29
+
30
+ def put(path,data=nil)
31
+ handle_response @access_token.put(path,(data ? data.to_json : nil),AGREE2_JSON_HEADERS )
32
+ end
33
+
34
+ def delete(path)
35
+ handle_response @access_token.delete(path,AGREE2_JSON_HEADERS)
36
+ end
37
+
38
+ # OAuth Stuff below here
39
+
40
+ # The AccessToken token
41
+ def token
42
+ @access_token.token
43
+ end
44
+
45
+ # The AccessToken secret
46
+ def secret
47
+ @access_token.secret
48
+ end
49
+
50
+ def path #:nodoc:
51
+ ""
52
+ end
53
+ protected
54
+
55
+ def handle_response(response)#:nodoc:
56
+ case response.code
57
+ when "200"
58
+ response.body
59
+ when "201"
60
+ response.body
61
+ when "302"
62
+ if response['Location']=~/(#{AGREE2_URL})\/(.*)$/
63
+ parts=$2.split('/')
64
+ (('Agree2::'+parts[0].classify).constantize).get self,parts[1]
65
+ else
66
+ #todo raise hell
67
+ end
68
+ else
69
+ response
70
+ end
71
+ end
72
+ end
73
+ end
data/lib/agree2.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'net/https'
2
+ require 'rubygems'
3
+ gem 'oauth', ">= 0.2.4"
4
+ require 'oauth'
5
+ gem 'json'
6
+ gem 'activesupport'
7
+ require 'activesupport'
8
+ AGREE2_ENV = :production unless defined?(AGREE2_ENV)
9
+ AGREE2_URL = (AGREE2_ENV==:development) ? 'http://agree2.dev' : 'https://agree2.com'
10
+ module Agree2
11
+ class Agree2Exception < RuntimeError #:nodoc:
12
+ end
13
+ end
14
+ require 'agree2/client'
15
+ require 'agree2/proxy_collection'
16
+ require 'agree2/user'
17
+ require 'agree2/base'
18
+ require 'agree2/agreement'
19
+ require 'agree2/party'
20
+ require 'agree2/template'
21
+
22
+
23
+
24
+ # FireEagle addition to the <code>OAuth::Consumer</code> class. Taken from Yahoo FireEagle GEM
25
+ class OAuth::Consumer
26
+ alias_method :create_http_with_verify, :create_http
27
+ # Monkey patch to silence the SSL warnings
28
+ def create_http_without_verify #:nodoc:
29
+ http_object = create_http_with_verify
30
+ http_object.verify_mode = OpenSSL::SSL::VERIFY_NONE if uri.scheme=="https"
31
+ http_object
32
+ end
33
+ alias_method :create_http, :create_http_without_verify
34
+ end
@@ -0,0 +1,190 @@
1
+ require File.join(File.dirname(__FILE__),"spec_helper")
2
+ describe Agree2::Agreement do
3
+ before(:each) do
4
+ @client=Agree2::Client.new "client_key","client_secret"
5
+ @user=Agree2::User.new(@client,"token","token_secret")
6
+ @json=IO.read(File.join(File.dirname(__FILE__),"fixtures","agreement.json"))
7
+ end
8
+
9
+ describe "Built from hash" do
10
+ before(:each) do
11
+ @agreement=Agree2::Agreement.new(@user,{:title=>"My Title",:body=>"My Body"})
12
+ end
13
+
14
+ it "should have attribute hash" do
15
+ @agreement.attributes.should=={:title=>"My Title",:body=>"My Body"}
16
+ end
17
+
18
+ it "should have a user" do
19
+ @agreement.user.should==@user
20
+ end
21
+
22
+ it "should have title" do
23
+ @agreement.title.should=="My Title"
24
+ end
25
+
26
+ it "should have body" do
27
+ @agreement.body.should=="My Body"
28
+ end
29
+
30
+ it "should be new record" do
31
+ @agreement.should be_new_record
32
+ end
33
+
34
+ describe "Save to Server" do
35
+ before(:each) do
36
+ @user.should_receive(:post).with("/agreements",{'agreement'=>{:title=>"My Title",:body=>"My Body"}}).and_return(@json)
37
+ end
38
+
39
+ it "should save and return true" do
40
+ @agreement.save
41
+ end
42
+
43
+ it "should no longer be new record" do
44
+ @agreement.save
45
+ @agreement.should_not be_new_record
46
+ end
47
+
48
+ it "should have a permalink" do
49
+ @agreement.save
50
+ @agreement.permalink.should_not be_nil
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "from json" do
56
+
57
+ before(:each) do
58
+ @agreement=Agree2::Agreement.new(@user,@json)
59
+ end
60
+
61
+ it "should have a user" do
62
+ @agreement.user.should==@user
63
+ end
64
+
65
+ it "should have title" do
66
+ @agreement.title.should=="Agreement to pay [currency=USD] [amount=100] to [recipient]"
67
+ end
68
+
69
+ it "should have permalink" do
70
+ @agreement.permalink.should=="hello"
71
+ end
72
+
73
+ it "should have a nil body" do
74
+ @agreement.body.should be_nil
75
+ end
76
+
77
+ it "should have a path" do
78
+ @agreement.path.should=="/agreements/hello"
79
+ end
80
+
81
+ it "should have a url" do
82
+ @agreement.to_url.should=="https://agree2.com/agreements/hello"
83
+ end
84
+
85
+ [:created_at,:updated_at,:fields,:state,:active_version,:version,
86
+ :digest,:finalized_at,:finalized_at,:terminated_at,:activated_at,:valid_to].each do |field|
87
+ it "should have #{field}" do
88
+ @agreement.send(field).should_not be_nil
89
+ end
90
+ end
91
+
92
+ it "should have fields" do
93
+ @agreement.fields.should=={
94
+ "amount"=> "100",
95
+ "currency"=> "USD"
96
+ }
97
+ end
98
+
99
+ it "should have methods defined for fields" do
100
+ @agreement.amount.should=="100"
101
+ @agreement.currency.should=="USD"
102
+ end
103
+ [:amount,:currency].each do |field|
104
+ it "should support setting the value" do
105
+ @agreement.send("#{field.to_s}=".to_sym,"test this")
106
+ @agreement.send(field).should=="test this"
107
+ @agreement.fields[field.to_s].should=="test this"
108
+ end
109
+
110
+ it "should have respond_to wired for field accessor" do
111
+ @agreement.respond_to?(field).should==true
112
+ end
113
+
114
+ it "should have respond_to wired for field setter" do
115
+ @agreement.respond_to?("#{field.to_s}=".to_sym).should==true
116
+ end
117
+ end
118
+
119
+ it "should not be new record" do
120
+ @agreement.should_not be_new_record
121
+ end
122
+
123
+ it "should reload" do
124
+ @user.should_receive(:get).with("/agreements/hello").and_return(@json)
125
+ @agreement.reload
126
+ end
127
+
128
+ it "should destroy" do
129
+ @user.should_receive(:delete).with("/agreements/hello").and_return("")
130
+ @agreement.destroy
131
+ end
132
+
133
+ it "should finalize" do
134
+ @user.should_receive(:post).with("/agreements/hello/finalize").and_return(" ")
135
+ @agreement.finalize!.should==true
136
+ end
137
+
138
+ describe "Save to Server" do
139
+ before(:each) do
140
+ @user.should_receive(:put).with("/agreements/hello",{"fields"=>@agreement.fields}).and_return(@json)
141
+ end
142
+
143
+ it "should save and return true" do
144
+ @agreement.save
145
+ end
146
+
147
+ it "should not a be new record" do
148
+ @agreement.save
149
+ @agreement.should_not be_new_record
150
+ end
151
+
152
+ it "should have a permalink" do
153
+ @agreement.save
154
+ @agreement.permalink.should_not be_nil
155
+ end
156
+ end
157
+ end
158
+
159
+ describe "load from server" do
160
+ before(:each) do
161
+ @user.should_receive(:get).with("/agreements/hello").and_return(@json)
162
+ end
163
+
164
+ def do_get
165
+ @agreement=Agree2::Agreement.get(@user,"hello")
166
+ end
167
+
168
+ it "should have a user" do
169
+ do_get
170
+ @agreement.user.should==@user
171
+ end
172
+
173
+ it "should have title" do
174
+ do_get
175
+ @agreement.title.should=="Agreement to pay [currency=USD] [amount=100] to [recipient]"
176
+ end
177
+
178
+ it "should have permalink" do
179
+ do_get
180
+ @agreement.permalink.should=="hello"
181
+ end
182
+
183
+ it "should not be new record" do
184
+ do_get
185
+ @agreement.should_not be_new_record
186
+ end
187
+
188
+ end
189
+ end
190
+
@@ -0,0 +1,49 @@
1
+ require File.join(File.dirname(__FILE__),"spec_helper")
2
+
3
+ describe Agree2::Client do
4
+
5
+ before(:each) do
6
+ @client=Agree2::Client.new "client_key","client_secret"
7
+ end
8
+
9
+ it "should contain key and secret" do
10
+ @client.key.should=="client_key"
11
+ @client.secret.should=="client_secret"
12
+ end
13
+
14
+ it "should have consumer" do
15
+ consumer=@client.consumer
16
+ consumer.key.should==@client.key
17
+ consumer.secret.should==@client.secret
18
+ consumer.site.should=="https://agree2.com"
19
+ end
20
+
21
+ it "should create user" do
22
+ user=@client.user("token","token_secret")
23
+ user.client.should==@client
24
+ user.access_token.consumer.should==@client.consumer
25
+ user.token.should=="token"
26
+ user.secret.should=="token_secret"
27
+ end
28
+
29
+ it "should create request token" do
30
+ request_token=mock("access_token")
31
+ @client.consumer.should_receive(:get_request_token).and_return(request_token)
32
+ token=@client.get_request_token
33
+ token.should==request_token
34
+ end
35
+
36
+ it "should create request token" do
37
+ request_token=mock("request_token")
38
+ access_token=mock("access_token")
39
+ access_token.stub!(:token).and_return('token')
40
+ access_token.stub!(:secret).and_return('token_secret')
41
+ request_token.should_receive(:get_access_token).and_return(access_token)
42
+ user=@client.user_from_request_token(request_token)
43
+ user.client.should==@client
44
+ user.access_token.consumer.should==@client.consumer
45
+ user.token.should=="token"
46
+ user.secret.should=="token_secret"
47
+ end
48
+
49
+ end
@@ -0,0 +1,50 @@
1
+ {
2
+ "permalink": "hello",
3
+ "valid_to": "2009/08/12 22:04:51 +0000",
4
+ "updated_at": "2008/08/12 22:04:51 +0000",
5
+ "activated_at": "2008/08/12 22:04:51 +0000",
6
+ "parties": [
7
+ {
8
+ "updated_at": "2008/08/12 22:04:51 +0000",
9
+ "invited_by_id": null,
10
+ "account_id": null,
11
+ "role": "client",
12
+ "organization_name": null,
13
+ "invited_at": null,
14
+ "id": 1102,
15
+ "agreement_id": 757,
16
+ "user_id": null,
17
+ "first_name": "Bob",
18
+ "accept_code": "d373b3dc17b7177aa0626e6ce7f449fcc3ca7c9c",
19
+ "last_name": "Wildcat",
20
+ "email": "bob@gmail.inv",
21
+ "created_at": "2008/08/12 22:04:51 +0000"},
22
+ {
23
+ "updated_at": "2008/08/12 22:04:51 +0000",
24
+ "invited_by_id": null,
25
+ "account_id": 1,
26
+ "role": "provider",
27
+ "organization_name": null,
28
+ "invited_at": null,
29
+ "id": 1103,
30
+ "agreement_id": 757,
31
+ "user_id": 1,
32
+ "first_name": "Pelle",
33
+ "accept_code": "39e6c8c02e8ee9efb7a3606a45b3e297ce616b55",
34
+ "last_name": "Braendgaard",
35
+ "email": "pelle@stakeventures.com",
36
+ "created_at": "2008/08/12 22:04:51 +0000"}
37
+ ],
38
+ "title": "Agreement to pay [currency=USD] [amount=100] to [recipient]",
39
+ "active_version": 1,
40
+ "smart_fields": {
41
+ "amount": "100",
42
+ "currency": "USD"
43
+ },
44
+ "terminated_at": "2008/08/12 22:04:51 +0000",
45
+ "digest": "70b2d46022ae44ea8041c7573e856a14b2186273",
46
+ "finalized_at": "2008/08/12 22:04:51 +0000",
47
+ "version": 1,
48
+ "description": "An agreement",
49
+ "state": "final",
50
+ "created_at": "2008/08/12 22:04:51 +0000"}
@@ -0,0 +1,15 @@
1
+ {
2
+ "updated_at": "2008/08/12 22:04:51 +0000",
3
+ "invited_by_id": null,
4
+ "account_id": null,
5
+ "role": "client",
6
+ "organization_name": null,
7
+ "invited_at": null,
8
+ "id": 1102,
9
+ "agreement_id": 757,
10
+ "user_id": null,
11
+ "first_name": "Bob",
12
+ "last_name": "Wildcat",
13
+ "email": "bob@gmail.inv",
14
+ "created_at": "2008/08/12 22:04:51 +0000"
15
+ }