databasedotcom 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,154 @@
1
+ =databasedotcom
2
+ databasedotcom is a gem to enable ruby applications to access the SalesForce REST API. If you use bundler, simply list it in your Gemfile, like so:
3
+
4
+ gem 'databasedotcom'
5
+
6
+ If you don't use bundler, install it by hand:
7
+
8
+ gem install databasedotcom
9
+
10
+ =Documentation
11
+
12
+ Reference documentation is available at rubydoc.info[http://rubydoc.info/github/heroku/databasedotcom/master/frames]
13
+
14
+ =Source
15
+
16
+ Source is available at github[http://github.com/heroku/databasedotcom]
17
+
18
+ = Usage
19
+ == Initialization
20
+ When you create a Databasedotcom::Client object, you need to configure it with a client id and client secret that corresponds to one of the Remote Access Applications configured within your SalesForce instance. The SalesForce UI refers to the client id as "Consumer Key", and to the client secret as "Consumer Secret".
21
+
22
+ You can configure your Client object with a client id and client secret in one of several different ways:
23
+ === Configuration from the environment
24
+ If configuration information is present in the environment, the new Client will take configuration information from there.
25
+
26
+ export DATABASEDOTCOM_CLIENT_ID=foo
27
+ export DATABASEDOTCOM_CLIENT_SECRET=bar
28
+
29
+ Then
30
+
31
+ client = Databasedotcom::Client.new
32
+ client.client_id #=> foo
33
+ client.client_secret #=> bar
34
+
35
+ === Configuration from a YAML file
36
+ If you pass the name of a YAML file when you create a Client, the new Client will read the YAML file and take the client id and client secret values from there.
37
+
38
+ # databasedotcom.yml
39
+ #
40
+ ---
41
+ client_secret: bro
42
+ client_id: baz
43
+
44
+ Then
45
+
46
+ client = Databasedotcom::Client.new("databasedotcom.yml")
47
+ client.client_id #=> bro
48
+ client.client_secret #=> baz
49
+
50
+ === Configuration from a Hash
51
+ If you pass a hash when you create a Client, the new Client will take configuration information from that Hash.
52
+
53
+ client = Databasedotcom::Client.new :client_id => "sponge", :client_secret => "bob"
54
+ client.client_id #=> sponge
55
+ client.client_secret #=> bob
56
+
57
+ === Configuration precedence
58
+ Configuration information present in the environment always takes precedence over that passed in via a YAML file or a Hash.
59
+
60
+ export DATABASEDOTCOM_CLIENT_ID=foo
61
+ export DATABASEDOTCOM_CLIENT_SECRET=bar
62
+
63
+ Then
64
+
65
+ client = Databasedotcom::Client.new :client_id => "sponge", :client_secret => "bob"
66
+ client.client_id #=> foo
67
+ client.client_secret #=> bar
68
+
69
+ === Usage in an application deployed on Heroku
70
+ You can use the <tt>heroku config:add</tt> command to set environment variables:
71
+
72
+ heroku config:add DATABASEDOTCOM_CIENT_ID=foo
73
+ heroku config:add DATABASEDOTCOM_CIENT_SECRET=bar
74
+
75
+ Then, when you create your client like:
76
+
77
+ client = Databasedotcom::Client.new
78
+
79
+ it will use the configuration information that you set with <tt>heroku config:add</tt>.
80
+
81
+ == Authentication
82
+ The first thing you need to do with the new Client is to authenticate with SalesForce. You can do this in one of several ways:
83
+
84
+ === Authentication via an externally-acquired OAuth access token
85
+ If you have acquired an OAuth access token for your SalesForce instance through some external means, you can use it. Note that you have to pass both the token and your SalesForce instance URL to the <tt>authenticate</tt> method:
86
+
87
+ client.authenticate :token => "my-oauth-token", :instance_url => "http://na1.salesforce.com" #=> "my-oauth-token"
88
+
89
+ === Authentication via Omniauth
90
+ If you are using the gem within the context of a web application, and your web app is using Omniauth to do OAuth with SalesForce, you can authentication the Client direction via the Hash that Omniauth passes to your OAuth callback method, like so:
91
+
92
+ client.authenticate request.env['omniauth.auth'] #=> "the-oauth-token"
93
+
94
+ === Authentication via username and password
95
+ You can authenticate your Client directly with SalesForce with a valid username and password for a user in your SalesForce instance. Note that, if access to your SalesForce instance requires a {security token}[http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_concepts_security.htm], the value that you pass for <tt>:password</tt> must be the password for the user concatenated with her security token.
96
+
97
+ client.authenticate :username => "foo@bar.com", :password => "ThePasswordTheSecurityToken" #=> "the-oauth-token"
98
+
99
+ == Accessing the Sobject API
100
+ You can retrieve a list of Sobject defined in your SalesForce instance like so:
101
+
102
+ client.list_sobjects #=> ['User', 'Group', 'Contact']
103
+
104
+ Once you have the name of an Sobject, the easiest way to interact with it is to first materialize it:
105
+
106
+ contact_class = client.materialize("Contact") #=> Contact
107
+
108
+ By default, Sobject classes are materialized into the global namespace- if you want materialize into another module, you can easily do configure this:
109
+
110
+ client.sobject_module = My::Module
111
+ client.materialize("Contact") #=> My::Module::Contact
112
+
113
+ Materialized Sobject classes behave much like ActiveRecord classes:
114
+
115
+ contact = Contact.find("contact_id") #=> #<Contact @Id="contact_id", ...>
116
+ contact = Contact.find_by_Name("John Smith") #=> dynamic finders!
117
+ contacts = Contact.all #=> a Databasedotcom::Collection of Contact instances
118
+ contacts = Contact.find_all_by_Company("IBM") #=> a Databasedotcom::Collection of matching Contacts
119
+ contact.Name #=> the contact's Name attribute
120
+ contact["Name"] #=> same thing
121
+ contact.Name = "new name" #=> change the contact's Name attribute, in memory
122
+ contact["Name"] = "new name" #=> same thing
123
+ contact.save #=> save the changes to the database
124
+ contact.update_attributes "Name" => "newer name",
125
+ "Phone" => "4156543210" #=> change several attributes at once and save them
126
+ contact.delete #=> delete the contact from the database
127
+
128
+ See the documentation for full details.
129
+
130
+ == Accessing the Chatter API
131
+ You can easily access Chatter feeds, group, conversations, etc.:
132
+
133
+ my_feed_items = Databasedotcom::Chatter::UserProfileFeed.find(client) #=> a Databasedotcom::Collection of FeedItems
134
+
135
+ my_feed_items.each do |feed_item|
136
+ feed_item.likes #=> a Databasedotcom::Collection of Like instances
137
+ feed_item.comments #=> a Databasedotcom::Collection of Comment instances
138
+ feed_item.raw_hash #=> the hash returned from the Chatter API describing this FeedItem
139
+ feed_item.comment("This is cool") #=> create a new comment on the FeedItem
140
+ feed_item.like #=> the authenticating user likes the FeedItem
141
+ end
142
+
143
+ me = Databasedotcom::Chatter::User.find(client, "me") #=> a User for the authenticating user
144
+ me.followers #=> a Databasedotcom::Collection of Users
145
+ me.post_status("what I'm doing now") #=> post a new status
146
+
147
+ you = Databasedotcom::Chatter::User.find(client, "your-user-id")
148
+ me.follow(you) #=> start following a user
149
+
150
+ See the documentation for full details.
151
+
152
+ = License
153
+
154
+ This gem is licensed under the MIT License.
@@ -0,0 +1,7 @@
1
+ require 'databasedotcom/version'
2
+ require 'databasedotcom/core_extensions'
3
+ require 'databasedotcom/client'
4
+ require 'databasedotcom/sales_force_error'
5
+ require 'databasedotcom/collection'
6
+ require 'databasedotcom/sobject'
7
+ require 'databasedotcom/chatter'
@@ -0,0 +1,11 @@
1
+ require 'databasedotcom/chatter/feeds'
2
+ require 'databasedotcom/chatter/filter_feed'
3
+ require 'databasedotcom/chatter/feed_item'
4
+ require 'databasedotcom/chatter/comment'
5
+ require 'databasedotcom/chatter/like'
6
+ require 'databasedotcom/chatter/user'
7
+ require 'databasedotcom/chatter/group'
8
+ require 'databasedotcom/chatter/group_membership'
9
+ require 'databasedotcom/chatter/subscription'
10
+ require 'databasedotcom/chatter/conversation'
11
+ require 'databasedotcom/chatter/message'
@@ -0,0 +1,10 @@
1
+ require 'databasedotcom/chatter/record'
2
+
3
+ module Databasedotcom
4
+ module Chatter
5
+
6
+ # A comment posted on a FeedItem.
7
+ class Comment < Record
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,100 @@
1
+ require 'databasedotcom/chatter/record'
2
+
3
+ module Databasedotcom
4
+ module Chatter
5
+ # A thread of private messages. When calling +Conversation.find+ or +Conversation.all+, you must pass +:user_id => <my_user_id>+ in the _parameters_
6
+ #
7
+ # Conversation.all(@client, :user_id => "me")
8
+ # Conversation.find(@client, "conversationId", :user_id => "f80ad89f9d98d89dfd89")
9
+ class Conversation < Record
10
+
11
+ # Creates a new Conversation and sets its +id+ and +url+ to values obtained from the server response.
12
+ def initialize(client, response)
13
+ super
14
+ @id ||= @raw_hash["conversationId"]
15
+ @url ||= @raw_hash["conversationUrl"]
16
+ end
17
+
18
+ # Find the Conversation identified by _cid_ and archive it. Returns the updated Conversation.
19
+ #
20
+ # Conversation.archive(@client, "fakeid")
21
+ def self.archive(client, cid)
22
+ url = "/services/data/v#{client.version}/chatter/users/me/conversations/#{cid}"
23
+ response = client.http_patch(url, nil, :archived => "true")
24
+ Conversation.new(client, response.body)
25
+ end
26
+
27
+ # Find the Conversation identified by _cid_ and unarchive it. Returns the updated Conversation.
28
+ #
29
+ # Conversation.unarchive(@client, "fakeid")
30
+ def self.unarchive(client, cid)
31
+ url = "/services/data/v#{client.version}/chatter/users/me/conversations/#{cid}"
32
+ response = client.http_patch(url, nil, :archived => "false")
33
+ Conversation.new(client, response.body)
34
+ end
35
+
36
+ # Find the Conversation identified by _cid_ and mark it as read. Returns the updated Conversation.
37
+ #
38
+ # Conversation.mark_read(@client, "fakeid")
39
+ def self.mark_read(client, cid)
40
+ url = "/services/data/v#{client.version}/chatter/users/me/conversations/#{cid}"
41
+ response = client.http_patch(url, nil, :read => "true")
42
+ Conversation.new(client, response.body)
43
+ end
44
+
45
+ # Find the Conversation identified by _cid_ and mark it as unread. Returns the updated Conversation.
46
+ #
47
+ # Conversation.mark_unread(@client, "fakeid")
48
+ def self.mark_unread(client, cid)
49
+ url = "/services/data/v#{client.version}/chatter/users/me/conversations/#{cid}"
50
+ response = client.http_patch(url, nil, :read => "false")
51
+ Conversation.new(client, response.body)
52
+ end
53
+
54
+ # Gets all messages for the Conversation specified by _cid_ and the User specified by _uid_. Returns a Collection of Message objects.
55
+ def self.messages(client, uid, cid)
56
+ conversation = self.find(client, cid, :user_id => uid)
57
+ collection = Databasedotcom::Collection.new(client, nil, conversation.raw_hash["messages"]["nextPageUrl"], conversation.raw_hash["messages"]["previousPageUrl"], conversation.raw_hash["messages"]["currentPageUrl"])
58
+ conversation.raw_hash["messages"]["messages"].each do |item|
59
+ collection << Message.new(client, item)
60
+ end
61
+ collection
62
+ end
63
+
64
+ # Archive this Conversation.
65
+ def archive
66
+ self.class.archive(self.client, self.id)
67
+ end
68
+
69
+ # Unarchive this Conversation.
70
+ def unarchive
71
+ self.class.unarchive(self.client, self.id)
72
+ end
73
+
74
+ # Mark this Conversation as read.
75
+ def mark_read
76
+ self.class.mark_read(self.client, self.id)
77
+ end
78
+
79
+ # Mark this Conversation as unread.
80
+ def mark_unread
81
+ self.class.mark_unread(self.client, self.id)
82
+ end
83
+
84
+ # Return a Collection of messages from this Conversation.
85
+ def messages
86
+ collection = Databasedotcom::Collection.new(client, nil, self.raw_hash["messages"]["nextPageUrl"], self.raw_hash["messages"]["previousPageUrl"], self.raw_hash["messages"]["currentPageUrl"])
87
+ self.raw_hash["messages"]["messages"].each do |item|
88
+ collection << Message.new(client, item)
89
+ end
90
+ collection
91
+ end
92
+
93
+ protected
94
+
95
+ def self.search_parameter_name
96
+ :Q
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,64 @@
1
+ require 'json'
2
+
3
+ module Databasedotcom
4
+ module Chatter
5
+ # Parent class of all feeds and inherits from Collection. This class is not intended to be instantiated. Methods should be called on subclasses, which are all are dynamically defined (except for FilterFeed). Defined feeds are *NewsFeed*, *UserProfileFeed*, *RecordFeed*, *ToFeed*, *PeopleFeed*, *GroupsFeed*, *FilesFeed*, *CompanyFeed*, and *FilterFeed*.
6
+ class Feed < Collection
7
+
8
+ # Returns an enumerable Feed of FeedItem objects that make up the feed with the specified _id_. Should not be called as a class method on Feed, but as a method on subclasses.
9
+ #
10
+ # NewsFeed.find(@client) #=> [#<FeedItem ...>, #<FeedItem ...>, ...]
11
+ # PeopleFeed.find(@client, "userid") #=> [#<FeedItem ...>, #<FeedItem ...>, ...]
12
+ # FilterFeed.find(@client, "me", "000") #=> [#<FeedItem ...>, #<FeedItem ...>, ...]
13
+ #
14
+ # _id_prefix_ is only applicable for FilterFeed.
15
+ def self.find(client, id="me", id_prefix=nil)
16
+ path_components = %w(services data)
17
+ path_components << "v#{client.version}"
18
+ path_components.concat(%w(chatter feeds))
19
+ path_components << feed_type
20
+ path_components << id unless feed_type == "company"
21
+ path_components << id_prefix
22
+ path_components << "feed-items"
23
+ path = "/" + path_components.compact.join('/')
24
+ result = client.http_get(path)
25
+ response = JSON.parse(result.body)
26
+ collection = self.new(client, nil, response["nextPageUrl"], response["previousPageUrl"], response["currentPageUrl"])
27
+ response["items"].each do |item|
28
+ collection << FeedItem.new(client, item)
29
+ end
30
+ collection
31
+ end
32
+
33
+ # Posts a FeedItem to a Feed specified by _user_id_. Should not be called as a class method on Feed, but as a method on subclasses.
34
+ #
35
+ # UserProfileFeed.post(@client, "me", :text => "This is a status update about Salesforce.", :url => "http://www.salesforce.com")
36
+ #
37
+ # Returns the newly created FeedItem.
38
+ def self.post(client, user_id, parameters)
39
+ url = "/services/data/v#{client.version}/chatter/feeds/#{feed_type}/#{user_id}/feed-items"
40
+ response = client.http_post(url, nil, parameters)
41
+ Databasedotcom::Chatter::FeedItem.new(client, response.body)
42
+ end
43
+
44
+ # Posts a file to a Feed specified by _user_id_. Should not be called as a class method on Feed, but as a method on subclasses.
45
+ #
46
+ # UserProfileFeed.post_file(@client, "me", File.open("MyFile"), "text/plain", "MyFile", :desc => "This is an uploaded text file.")
47
+ #
48
+ # Returns the newly created FeedItem.
49
+ def self.post_file(client, user_id, io, file_type, file_name, parameters={})
50
+ url = "/services/data/v#{client.version}/chatter/feeds/#{feed_type}/#{user_id}/feed-items"
51
+ response = client.http_multipart_post(url, {"feedItemFileUpload" => UploadIO.new(io, file_type, file_name), "fileName" => file_name}, parameters)
52
+ Databasedotcom::Chatter::FeedItem.new(client, response.body)
53
+ end
54
+
55
+ private
56
+
57
+ def self.feed_type
58
+ self.name.match(/.+::(.+)Feed$/)[1].resourcerize
59
+ end
60
+ end
61
+
62
+ FEED_TYPES = %w(News UserProfile Record To People Groups Files Company)
63
+ end
64
+ end
@@ -0,0 +1,40 @@
1
+ require 'databasedotcom/chatter/record'
2
+
3
+ module Databasedotcom
4
+ module Chatter
5
+
6
+ # An item in a Feed.
7
+ class FeedItem < Record
8
+
9
+ # Returns a Collection of comments that were posted on this FeedItem instance.
10
+ def comments
11
+ collection = Databasedotcom::Collection.new(self.client, self.raw_hash["comments"]["total"], self.raw_hash["comments"]["nextPageUrl"], nil, self.raw_hash["comments"]["currentPageUrl"])
12
+ collection.concat(self.raw_hash["comments"]["comments"])
13
+ end
14
+
15
+ # Returns a Collection of likes for this FeedItem instance.
16
+ def likes
17
+ collection = Databasedotcom::Collection.new(self.client, self.raw_hash["likes"]["total"], self.raw_hash["likes"]["nextPageUrl"], self.raw_hash["likes"]["previousPageUrl"], self.raw_hash["likes"]["currentPageUrl"])
18
+ collection.concat(self.raw_hash["likes"]["likes"])
19
+ end
20
+
21
+ # Like this FeedItem.
22
+ def like
23
+ result = self.client.http_post("/services/data/v#{self.client.version}/chatter/feed-items/#{self.id}/likes")
24
+ Like.new(self.client, result.body)
25
+ end
26
+
27
+ # Post a Comment on this FeedItem with content _text_.
28
+ def comment(text)
29
+ result = self.client.http_post("/services/data/v#{self.client.version}/chatter/feed-items/#{self.id}/comments", nil, :text => text)
30
+ Comment.new(self.client, result.body)
31
+ end
32
+
33
+ protected
34
+
35
+ def self.collection_from_response(response)
36
+ response["items"]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ require 'databasedotcom/chatter/feed'
2
+
3
+ Databasedotcom::Chatter::FEED_TYPES.each do |feed_type|
4
+ Databasedotcom::Chatter.const_set("#{feed_type}Feed", Class.new(Databasedotcom::Chatter::Feed))
5
+ end
@@ -0,0 +1,14 @@
1
+ module Databasedotcom
2
+ module Chatter
3
+ # Filter feeds contain items pertaining to both a user and another specified resource.
4
+ class FilterFeed < Feed
5
+
6
+ # Lists all FilterFeeds for the user with id _user_id_.
7
+ def self.feeds(client, user_id="me")
8
+ url = "/services/data/v#{client.version}/chatter/feeds/filter/#{user_id}"
9
+ result = client.http_get(url)
10
+ JSON.parse(result.body)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ require 'databasedotcom/chatter/record'
2
+ require 'databasedotcom/chatter/photo_methods'
3
+
4
+ module Databasedotcom
5
+ module Chatter
6
+ # A group of Users
7
+ class Group < Record
8
+ include PhotoMethods
9
+
10
+ # Returns a Collection of GroupMembership instances for the Group identified by _group_id_.
11
+ def self.members(client, group_id)
12
+ url = "/services/data/v#{client.version}/chatter/groups/#{group_id}/members"
13
+ result = client.http_get(url)
14
+ response = JSON.parse(result.body)
15
+ collection = Databasedotcom::Collection.new(client, response["totalMemberCount"], response["nextPageUrl"], response["previousPageUrl"], response["currentPageUrl"])
16
+ response["members"].each do |member|
17
+ collection << GroupMembership.new(client, member)
18
+ end
19
+ collection
20
+ end
21
+
22
+ # Join the group identified by _group_id_ as the user identified by _user_id_.
23
+ def self.join(client, group_id, user_id="me")
24
+ url = "/services/data/v#{client.version}/chatter/groups/#{group_id}/members"
25
+ response = client.http_post(url, nil, :userId => user_id)
26
+ GroupMembership.new(client, response.body)
27
+ end
28
+
29
+ # Get a Collection of GroupMembership objects for this Group. Always makes a call to the server.
30
+ def members!
31
+ self.class.members(self.client, self.id)
32
+ end
33
+
34
+ # Get a Collection of GroupMembership objects for this Group. Returns cached data if it has been called before.
35
+ def members
36
+ @members ||= members!
37
+ end
38
+
39
+ # Join this Group as the user identified by _user_id_.
40
+ def join(user_id="me")
41
+ self.class.join(self.client, self.id, user_id)
42
+ end
43
+ end
44
+ end
45
+ end