agree2 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }