force 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +107 -0
- data/Guardfile +8 -0
- data/LICENSE +22 -0
- data/README.md +421 -0
- data/Rakefile +10 -0
- data/coverage/assets/0.7.1/application.css +1110 -0
- data/coverage/assets/0.7.1/application.js +626 -0
- data/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
- data/coverage/assets/0.7.1/favicon_green.png +0 -0
- data/coverage/assets/0.7.1/favicon_red.png +0 -0
- data/coverage/assets/0.7.1/favicon_yellow.png +0 -0
- data/coverage/assets/0.7.1/loading.gif +0 -0
- data/coverage/assets/0.7.1/magnify.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/index.html +19808 -0
- data/force.gemspec +27 -0
- data/lib/force.rb +74 -0
- data/lib/force/abstract_client.rb +9 -0
- data/lib/force/attachment.rb +21 -0
- data/lib/force/client.rb +3 -0
- data/lib/force/collection.rb +45 -0
- data/lib/force/concerns/api.rb +321 -0
- data/lib/force/concerns/authentication.rb +39 -0
- data/lib/force/concerns/base.rb +59 -0
- data/lib/force/concerns/caching.rb +24 -0
- data/lib/force/concerns/canvas.rb +10 -0
- data/lib/force/concerns/connection.rb +74 -0
- data/lib/force/concerns/picklists.rb +87 -0
- data/lib/force/concerns/streaming.rb +31 -0
- data/lib/force/concerns/verbs.rb +67 -0
- data/lib/force/config.rb +140 -0
- data/lib/force/data/client.rb +18 -0
- data/lib/force/mash.rb +66 -0
- data/lib/force/middleware.rb +27 -0
- data/lib/force/middleware/authentication.rb +73 -0
- data/lib/force/middleware/authentication/password.rb +17 -0
- data/lib/force/middleware/authentication/token.rb +15 -0
- data/lib/force/middleware/authorization.rb +15 -0
- data/lib/force/middleware/caching.rb +22 -0
- data/lib/force/middleware/gzip.rb +31 -0
- data/lib/force/middleware/instance_url.rb +14 -0
- data/lib/force/middleware/logger.rb +40 -0
- data/lib/force/middleware/mashify.rb +16 -0
- data/lib/force/middleware/multipart.rb +55 -0
- data/lib/force/middleware/raise_error.rb +25 -0
- data/lib/force/signed_request.rb +48 -0
- data/lib/force/sobject.rb +68 -0
- data/lib/force/tooling/client.rb +11 -0
- data/lib/force/upload_io.rb +20 -0
- data/lib/force/version.rb +3 -0
- data/spec/fixtures/auth_error_response.json +4 -0
- data/spec/fixtures/auth_success_response.json +7 -0
- data/spec/fixtures/blob.jpg +0 -0
- data/spec/fixtures/expired_session_response.json +6 -0
- data/spec/fixtures/reauth_success_response.json +7 -0
- data/spec/fixtures/refresh_error_response.json +4 -0
- data/spec/fixtures/refresh_success_response.json +7 -0
- data/spec/fixtures/services_data_success_response.json +12 -0
- data/spec/fixtures/sobject/create_success_response.json +5 -0
- data/spec/fixtures/sobject/delete_error_response.json +1 -0
- data/spec/fixtures/sobject/describe_sobjects_success_response.json +31 -0
- data/spec/fixtures/sobject/list_sobjects_success_response.json +31 -0
- data/spec/fixtures/sobject/org_query_response.json +11 -0
- data/spec/fixtures/sobject/query_aggregate_success_response.json +23 -0
- data/spec/fixtures/sobject/query_empty_response.json +5 -0
- data/spec/fixtures/sobject/query_error_response.json +6 -0
- data/spec/fixtures/sobject/query_paginated_first_page_response.json +14 -0
- data/spec/fixtures/sobject/query_paginated_last_page_response.json +13 -0
- data/spec/fixtures/sobject/query_success_response.json +38 -0
- data/spec/fixtures/sobject/recent_success_response.json +18 -0
- data/spec/fixtures/sobject/search_error_response.json +6 -0
- data/spec/fixtures/sobject/search_success_response.json +16 -0
- data/spec/fixtures/sobject/sobject_describe_error_response.json +6 -0
- data/spec/fixtures/sobject/sobject_describe_success_response.json +1429 -0
- data/spec/fixtures/sobject/sobject_find_error_response.json +6 -0
- data/spec/fixtures/sobject/sobject_find_success_response.json +29 -0
- data/spec/fixtures/sobject/upsert_created_success_response.json +5 -0
- data/spec/fixtures/sobject/upsert_error_response.json +6 -0
- data/spec/fixtures/sobject/upsert_multiple_error_response.json +4 -0
- data/spec/fixtures/sobject/upsert_updated_success_response.json +0 -0
- data/spec/fixtures/sobject/write_error_response.json +6 -0
- data/spec/integration/abstract_client_spec.rb +306 -0
- data/spec/integration/data/client_spec.rb +90 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/client_integration.rb +45 -0
- data/spec/support/concerns.rb +18 -0
- data/spec/support/event_machine.rb +14 -0
- data/spec/support/fixture_helpers.rb +45 -0
- data/spec/support/matchers.rb +11 -0
- data/spec/support/middleware.rb +76 -0
- data/spec/support/mock_cache.rb +13 -0
- data/spec/unit/abstract_client_spec.rb +11 -0
- data/spec/unit/attachment_spec.rb +15 -0
- data/spec/unit/collection_spec.rb +52 -0
- data/spec/unit/concerns/api_spec.rb +244 -0
- data/spec/unit/concerns/authentication_spec.rb +98 -0
- data/spec/unit/concerns/base_spec.rb +42 -0
- data/spec/unit/concerns/caching_spec.rb +29 -0
- data/spec/unit/concerns/canvas_spec.rb +30 -0
- data/spec/unit/concerns/connection_spec.rb +22 -0
- data/spec/unit/config_spec.rb +99 -0
- data/spec/unit/data/client_spec.rb +10 -0
- data/spec/unit/mash_spec.rb +36 -0
- data/spec/unit/middleware/authentication/password_spec.rb +31 -0
- data/spec/unit/middleware/authentication/token_spec.rb +24 -0
- data/spec/unit/middleware/authentication_spec.rb +67 -0
- data/spec/unit/middleware/authorization_spec.rb +11 -0
- data/spec/unit/middleware/gzip_spec.rb +66 -0
- data/spec/unit/middleware/instance_url_spec.rb +24 -0
- data/spec/unit/middleware/logger_spec.rb +19 -0
- data/spec/unit/middleware/mashify_spec.rb +11 -0
- data/spec/unit/middleware/raise_error_spec.rb +32 -0
- data/spec/unit/signed_request_spec.rb +24 -0
- data/spec/unit/sobject_spec.rb +86 -0
- data/spec/unit/tooling/client_spec.rb +7 -0
- data/tmp/rspec_guard_result +1 -0
- metadata +383 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d950e4cd2654615f569824faddc3e9c5cab80ce4
|
4
|
+
data.tar.gz: 5db2eece0b302ea2500940527bd14d35db0a169a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 63c2e2a59ad018d5f21cb5c8c6c72aff006b39643164a79e23dfadbfbb686e9334184c55722f038f812dc8031bee8cc447f82e36ca6a006d485fe5915c4a552f
|
7
|
+
data.tar.gz: 27fe7af0715d915c42352a1b9b7dd116f456cfb915242816d7c6a5e9ecba614837fcd7cfd49d87a7616f27fca7a960889a86fba77ccb0274fb305c7e1784f1b0
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
## 1.4.1 (Jun 18, 2013)
|
2
|
+
|
3
|
+
* Fixed a bug with HTTP 413 responses #75 @patronmanager
|
4
|
+
|
5
|
+
## 1.4.0 (Jun 9, 2013)
|
6
|
+
|
7
|
+
* Added support for the tooling API.
|
8
|
+
* Fixed a bug with EMSynchrony adapter.
|
9
|
+
* Added proxy support.
|
10
|
+
|
11
|
+
## 1.3.0 (Apr 6, 2013)
|
12
|
+
|
13
|
+
* Added support for lazily traversing paginated collections #61 by @nahiluhmot.
|
14
|
+
|
15
|
+
## 1.2.0 (Mar 30, 2013)
|
16
|
+
|
17
|
+
* Added support for proxies #60 by @wazoo.
|
18
|
+
|
19
|
+
## 1.1.0 (Mar 3, 2013)
|
20
|
+
|
21
|
+
* Added ability to download attachments easily.
|
22
|
+
|
23
|
+
Example
|
24
|
+
|
25
|
+
attachment = client.query('select Id, Name, Body from Attachment').first
|
26
|
+
File.open(attachment.Name, 'wb') { |f| f.write(attachment.Body) }
|
27
|
+
|
28
|
+
## 1.0.6 (Feb 16, 2013)
|
29
|
+
|
30
|
+
* Added `url` method.
|
31
|
+
|
32
|
+
Example
|
33
|
+
|
34
|
+
# Url to a record id
|
35
|
+
client.url('0013000000rRz')
|
36
|
+
# => https://na1.salesforce.com/0013000000rRz
|
37
|
+
|
38
|
+
# Url to an object that responds to `to_sparam`
|
39
|
+
record = Struct.new(:to_sparam).new('0013000000rRz')
|
40
|
+
client.url('0013000000rRz')
|
41
|
+
# => https://na1.salesforce.com/0013000000rRz
|
42
|
+
|
43
|
+
|
44
|
+
## 1.0.5 (Jan 11, 2013)
|
45
|
+
|
46
|
+
* Added `picklist_values` method.
|
47
|
+
|
48
|
+
Example
|
49
|
+
|
50
|
+
client.picklist_values('Account', 'Type')
|
51
|
+
|
52
|
+
client.picklist_values('Automobile__c', 'Model__c', :valid_for => 'Honda')
|
53
|
+
|
54
|
+
* Added CHANGELOG.md
|
55
|
+
|
56
|
+
## 1.0.4 (Jan 8, 2013)
|
57
|
+
|
58
|
+
* `Force::Client#inspect` now only prints out the options and not the
|
59
|
+
Faraday connection.
|
60
|
+
|
61
|
+
* The Faraday adapter is now configurabled:
|
62
|
+
|
63
|
+
Example:
|
64
|
+
|
65
|
+
Force.configure do |config|
|
66
|
+
config.adapter = :excon
|
67
|
+
end
|
68
|
+
|
69
|
+
* The http connection read/open timeout is now configurabled.
|
70
|
+
|
71
|
+
Example:
|
72
|
+
|
73
|
+
Force.configure do |config|
|
74
|
+
config.timeout = 300
|
75
|
+
end
|
76
|
+
|
77
|
+
## 1.0.3 (Jan 7, 2013)
|
78
|
+
|
79
|
+
* Fixed typo in method call.
|
80
|
+
|
81
|
+
## 1.0.2 (Jan 7, 2013)
|
82
|
+
|
83
|
+
* Minor cleanup.
|
84
|
+
* Moved decoding of signed requests into it's own class.
|
85
|
+
|
86
|
+
## 1.0.1 (Dec 31, 2012)
|
87
|
+
|
88
|
+
* `username`, `password`, `security_token`, `client_id` and `client_secret`
|
89
|
+
options now obtain defaults from environment variables.
|
90
|
+
* Add `head` verb.
|
91
|
+
|
92
|
+
## 1.0.0 (Dec 23, 2012)
|
93
|
+
|
94
|
+
* Default api version changed from 24.0 to 26.0.
|
95
|
+
* Fixed tests for streaming api to work with latest versions of faye.
|
96
|
+
* Added .find method to obtain all fields from an sobject.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
force (0.0.1)
|
5
|
+
faraday (~> 0.8.4)
|
6
|
+
faraday_middleware (>= 0.8.8)
|
7
|
+
hashie (>= 1.2.0, < 2.1)
|
8
|
+
json (>= 1.7.5, < 1.9.0)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.3.5)
|
14
|
+
coderay (1.0.9)
|
15
|
+
cookiejar (0.3.0)
|
16
|
+
crack (0.4.1)
|
17
|
+
safe_yaml (~> 0.9.0)
|
18
|
+
diff-lcs (1.2.4)
|
19
|
+
em-http-request (1.1.1)
|
20
|
+
addressable (>= 2.3.4)
|
21
|
+
cookiejar
|
22
|
+
em-socksify (>= 0.3)
|
23
|
+
eventmachine (>= 1.0.3)
|
24
|
+
http_parser.rb (>= 0.6.0.beta.2)
|
25
|
+
em-socksify (0.3.0)
|
26
|
+
eventmachine (>= 1.0.0.beta.4)
|
27
|
+
eventmachine (1.0.3)
|
28
|
+
faraday (0.8.8)
|
29
|
+
multipart-post (~> 1.2.0)
|
30
|
+
faraday_middleware (0.9.0)
|
31
|
+
faraday (>= 0.7.4, < 0.9)
|
32
|
+
faye (1.0.0)
|
33
|
+
cookiejar (>= 0.3.0)
|
34
|
+
em-http-request (>= 0.3.0)
|
35
|
+
eventmachine (>= 0.12.0)
|
36
|
+
faye-websocket (>= 0.7.0)
|
37
|
+
multi_json (>= 1.0.0)
|
38
|
+
rack (>= 1.0.0)
|
39
|
+
websocket-driver (>= 0.3.0)
|
40
|
+
faye-websocket (0.7.0)
|
41
|
+
eventmachine (>= 0.12.0)
|
42
|
+
websocket-driver (>= 0.3.0)
|
43
|
+
ffi (1.9.0)
|
44
|
+
formatador (0.2.4)
|
45
|
+
guard (1.8.3)
|
46
|
+
formatador (>= 0.2.4)
|
47
|
+
listen (~> 1.3)
|
48
|
+
lumberjack (>= 1.0.2)
|
49
|
+
pry (>= 0.9.10)
|
50
|
+
thor (>= 0.14.6)
|
51
|
+
guard-rspec (3.1.0)
|
52
|
+
guard (>= 1.8)
|
53
|
+
rspec (~> 2.13)
|
54
|
+
hashie (2.0.5)
|
55
|
+
http_parser.rb (0.6.0.beta.2)
|
56
|
+
json (1.8.0)
|
57
|
+
listen (1.3.1)
|
58
|
+
rb-fsevent (>= 0.9.3)
|
59
|
+
rb-inotify (>= 0.9)
|
60
|
+
rb-kqueue (>= 0.2)
|
61
|
+
lumberjack (1.0.4)
|
62
|
+
method_source (0.8.2)
|
63
|
+
multi_json (1.8.0)
|
64
|
+
multipart-post (1.2.0)
|
65
|
+
pry (0.9.12.2)
|
66
|
+
coderay (~> 1.0.5)
|
67
|
+
method_source (~> 0.8)
|
68
|
+
slop (~> 3.4)
|
69
|
+
rack (1.5.2)
|
70
|
+
rake (10.1.0)
|
71
|
+
rb-fsevent (0.9.3)
|
72
|
+
rb-inotify (0.9.2)
|
73
|
+
ffi (>= 0.5.0)
|
74
|
+
rb-kqueue (0.2.0)
|
75
|
+
ffi (>= 0.5.0)
|
76
|
+
rspec (2.14.1)
|
77
|
+
rspec-core (~> 2.14.0)
|
78
|
+
rspec-expectations (~> 2.14.0)
|
79
|
+
rspec-mocks (~> 2.14.0)
|
80
|
+
rspec-core (2.14.5)
|
81
|
+
rspec-expectations (2.14.3)
|
82
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
83
|
+
rspec-mocks (2.14.3)
|
84
|
+
safe_yaml (0.9.7)
|
85
|
+
simplecov (0.7.1)
|
86
|
+
multi_json (~> 1.0)
|
87
|
+
simplecov-html (~> 0.7.1)
|
88
|
+
simplecov-html (0.7.1)
|
89
|
+
slop (3.4.6)
|
90
|
+
thor (0.18.1)
|
91
|
+
webmock (1.13.0)
|
92
|
+
addressable (>= 2.2.7)
|
93
|
+
crack (>= 0.3.2)
|
94
|
+
websocket-driver (0.3.0)
|
95
|
+
|
96
|
+
PLATFORMS
|
97
|
+
ruby
|
98
|
+
|
99
|
+
DEPENDENCIES
|
100
|
+
faye
|
101
|
+
force!
|
102
|
+
guard-rspec
|
103
|
+
jruby-openssl
|
104
|
+
rake
|
105
|
+
rspec (~> 2.14.0)
|
106
|
+
simplecov (~> 0.7.1)
|
107
|
+
webmock (~> 1.13.0)
|
data/Guardfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
guard 'rspec', all_on_start: false, all_after_pass: false do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch('spec/spec_helper.rb') { "spec" }
|
4
|
+
|
5
|
+
watch(%r{^lib/force/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
6
|
+
watch(%r{^lib/force/(.+)\.rb$}) { |m| "spec/integration/#{m[1]}_spec.rb" }
|
7
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
8
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Eric J. Holmes
|
2
|
+
|
3
|
+
With portions Copyright (c) 2013 Heroku (http://heroku.com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,421 @@
|
|
1
|
+
# Force
|
2
|
+
|
3
|
+
_A ruby gem for the [Salesforce REST api](http://www.salesforce.com/us/developer/docs/api_rest/index.htm)._
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- A clean and modular architecture using [Faraday middleware](https://github.com/technoweenie/faraday) and [Hashie::Mash](https://github.com/intridea/hashie/tree/v1.2.0)'d responses.
|
8
|
+
- Support for interacting with multiple users from different orgs.
|
9
|
+
- Support for parent-to-child relationships.
|
10
|
+
- Support for aggregate queries.
|
11
|
+
- Support for the [Streaming API](#streaming)
|
12
|
+
- Support for blob data types.
|
13
|
+
- Support for GZIP compression.
|
14
|
+
- Support for [custom Apex REST endpoints](#custom-apex-rest-endpoints).
|
15
|
+
- Support for dependent picklists.
|
16
|
+
- Support for decoding [Force.com Canvas](http://www.salesforce.com/us/developer/docs/platform_connectpre/canvas_framework.pdf) signed requests. (NEW!)
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
gem 'force'
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install force
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
Force is designed with flexibility and ease of use in mind. By default, all api calls will
|
35
|
+
return [Hashie::Mash](https://github.com/intridea/hashie/tree/v1.2.0) objects,
|
36
|
+
so you can do things like `client.query('select Id, (select Name from Children__r) from Account').Children__r.first.Name`.
|
37
|
+
|
38
|
+
### Initialization
|
39
|
+
|
40
|
+
Which authentication method you use really depends on your use case. If you're
|
41
|
+
building an application where many users from different orgs are authenticated
|
42
|
+
through oauth and you need to interact with data in their org on their behalf,
|
43
|
+
you should use the OAuth token authentication method.
|
44
|
+
|
45
|
+
If you're using the gem to interact with a single org (maybe you're building some
|
46
|
+
salesforce integration internally?) then you should use the username/password
|
47
|
+
authentication method.
|
48
|
+
|
49
|
+
#### OAuth Token Authentication
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
client = Force.new :instance_url => 'xx.salesforce.com',
|
53
|
+
:oauth_token => '...'
|
54
|
+
```
|
55
|
+
|
56
|
+
Although the above will work, you'll probably want to take advantage of the
|
57
|
+
(re)authentication middleware by specifying a refresh token, client id and client secret:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
client = Force.new :instance_url => 'xx.salesforce.com',
|
61
|
+
:oauth_token => '...',
|
62
|
+
:refresh_token => '...',
|
63
|
+
:client_id => '...',
|
64
|
+
:client_secret => '...'
|
65
|
+
```
|
66
|
+
|
67
|
+
#### Username/Password authentication
|
68
|
+
|
69
|
+
If you prefer to use a username and password to authenticate:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
client = Force.new :username => 'user@example.com',
|
73
|
+
:password => '...',
|
74
|
+
:security_token => '...',
|
75
|
+
:client_id => '...',
|
76
|
+
:client_secret => '...'
|
77
|
+
```
|
78
|
+
|
79
|
+
You can also set the username, password, security token, client id and client
|
80
|
+
secret in environment variables:
|
81
|
+
|
82
|
+
```bash
|
83
|
+
export SALESFORCE_USERNAME="username"
|
84
|
+
export SALESFORCE_PASSWORD="password"
|
85
|
+
export SALESFORCE_SECURITY_TOKEN="security token"
|
86
|
+
export SALESFORCE_CLIENT_ID="client id"
|
87
|
+
export SALESFORCE_CLIENT_SECRET="client secret"
|
88
|
+
```
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
client = Force.new
|
92
|
+
```
|
93
|
+
|
94
|
+
### Proxy Support
|
95
|
+
|
96
|
+
You can specify a http proxy using the :proxy_uri option, as follows:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
client = Force.new :proxy_uri => 'http://proxy.example.com:123'
|
100
|
+
```
|
101
|
+
|
102
|
+
This paramter also will accept `http://user@password:proxy.example.com:123` or using the environemnt variable `PROXY_URI`.
|
103
|
+
|
104
|
+
#### Sandbox Orgs
|
105
|
+
|
106
|
+
You can connect to sandbox orgs by specifying a host. The default host is
|
107
|
+
`login.salesforce.com`:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
client = Force.new :host => 'test.salesforce.com'
|
111
|
+
```
|
112
|
+
The host can also be set with the environment variable `SALESFORCE_HOST`.
|
113
|
+
|
114
|
+
#### Global Configuration
|
115
|
+
|
116
|
+
You can set any of the options passed into Force.new globally:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
Force.configure do |config|
|
120
|
+
config.client_id = 'foo'
|
121
|
+
config.client_secret = 'bar'
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
---
|
126
|
+
|
127
|
+
### query
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
accounts = client.query("select Id, Something__c from Account where Id = 'someid'")
|
131
|
+
# => #<Force::Collection >
|
132
|
+
|
133
|
+
account = accounts.first
|
134
|
+
# => #<Force::SObject >
|
135
|
+
|
136
|
+
account.sobject_type
|
137
|
+
# => 'Account'
|
138
|
+
|
139
|
+
account.Id
|
140
|
+
# => "someid"
|
141
|
+
|
142
|
+
account.Name = 'Foobar'
|
143
|
+
account.save
|
144
|
+
# => true
|
145
|
+
|
146
|
+
account.destroy
|
147
|
+
# => true
|
148
|
+
```
|
149
|
+
|
150
|
+
### find
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
client.find('Account', '001D000000INjVe')
|
154
|
+
# => #<Force::SObject Id="001D000000INjVe" Name="Test" LastModifiedBy="005G0000002f8FHIAY" ... >
|
155
|
+
|
156
|
+
client.find('Account', '1234', 'Some_External_Id_Field__c')
|
157
|
+
# => #<Force::SObject Id="001D000000INjVe" Name="Test" LastModifiedBy="005G0000002f8FHIAY" ... >
|
158
|
+
```
|
159
|
+
|
160
|
+
### search
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
# Find all occurrences of 'bar'
|
164
|
+
client.search('FIND {bar}')
|
165
|
+
# => #<Force::Collection >
|
166
|
+
|
167
|
+
# Find accounts match the term 'genepoint' and return the Name field
|
168
|
+
client.search('FIND {genepoint} RETURNING Account (Name)').map(&:Name)
|
169
|
+
# => ['GenePoint']
|
170
|
+
```
|
171
|
+
|
172
|
+
### create
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
client.create('Account', Name: 'Foobar Inc.') # => '0016000000MRatd'
|
176
|
+
```
|
177
|
+
|
178
|
+
### update
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
client.update('Account', Id: '0016000000MRatd', Name: 'Whizbang Corp') # => true
|
182
|
+
```
|
183
|
+
|
184
|
+
### upsert
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
client.upsert('Account', 'External__c', External__c: 12, Name: 'Foobar') # => true
|
188
|
+
```
|
189
|
+
|
190
|
+
### destroy
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
client.destroy('Account', '0016000000MRatd') # => true
|
194
|
+
```
|
195
|
+
|
196
|
+
> All the CRUD methods (`create`, `update`, `upsert`, `destroy`) have equivalent methods with a ! at the end (`create!`, `update!`, `upsert!`, `destroy!`), which can be used if you need to do some custom error handling. The bang methods will raise exceptions, while the
|
197
|
+
non-bang methods will return false in the event that an exception is raised.
|
198
|
+
|
199
|
+
### describe
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
client.describe # => { ... }
|
203
|
+
client.describe('Account') # => { ... }
|
204
|
+
```
|
205
|
+
|
206
|
+
### describe_layouts
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
client.describe_layout('Account') # => { ... }
|
210
|
+
client.describe_layouts('Account', '012E0000000RHEp') # => { ... }
|
211
|
+
```
|
212
|
+
|
213
|
+
### picklist_values
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
client.picklist_values('Account', 'Type') # => [#<Force::Mash label="Prospect" value="Prospect">]
|
217
|
+
|
218
|
+
# Given a custom object named Automobile__c
|
219
|
+
# with picklist fields Model__c and Make__c,
|
220
|
+
# where Model__c depends on the value of Make__c.
|
221
|
+
client.picklist_values('Automobile__c', 'Model__c', :valid_for => 'Honda')
|
222
|
+
# => [#<Force::Mash label="Civic" value="Civic">, ... ]
|
223
|
+
```
|
224
|
+
|
225
|
+
---
|
226
|
+
|
227
|
+
### authenticate!
|
228
|
+
|
229
|
+
Performs an authentication and returns the response. In general, calling this
|
230
|
+
directly shouldn't be required, since the client will handle authentication for
|
231
|
+
you automatically. This should only be used if you want to force
|
232
|
+
an authentication before using the streaming api, or you want to get some
|
233
|
+
information about the user.
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
response = client.authenticate!
|
237
|
+
# => #<Force::Mash access_token="..." id="https://login.salesforce.com/id/00DE0000000cOGcMAM/005E0000001eM4LIAU" instance_url="https://na9.salesforce.com" issued_at="1348465359751" scope="api refresh_token" signature="3fW0pC/TEY2cjK5FCBFOZdjRtCfAuEbK1U74H/eF+Ho=">
|
238
|
+
|
239
|
+
# Get the user information
|
240
|
+
info = client.get(response.id).body
|
241
|
+
info.user_id
|
242
|
+
# => '005E0000001eM4LIAU'
|
243
|
+
```
|
244
|
+
|
245
|
+
### File Uploads
|
246
|
+
|
247
|
+
Using the new [Blob Data](http://www.salesforce.com/us/developer/docs/api_rest/Content/dome_sobject_insert_update_blob.htm) api feature (500mb limit):
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
image = Force::UploadIO.new(File.expand_path('image.jpg', __FILE__), 'image/jpeg')
|
251
|
+
client.create 'Document', FolderId: '00lE0000000FJ6H',
|
252
|
+
Description: 'Document test',
|
253
|
+
Name: 'My image',
|
254
|
+
Body: image)
|
255
|
+
```
|
256
|
+
|
257
|
+
Using base64-encoded data _(37.5mb limit)_:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
data = Base64::encode64(File.read('image.jpg')
|
261
|
+
client.create 'Document', FolderId: '00lE0000000FJ6H',
|
262
|
+
Description: 'Document test',
|
263
|
+
Name: 'My image',
|
264
|
+
Body: data)
|
265
|
+
```
|
266
|
+
|
267
|
+
> See also: http://www.salesforce.com/us/developer/docs/api_rest/Content/dome_sobject_insert_update_blob.htm
|
268
|
+
|
269
|
+
### Downloading Attachments
|
270
|
+
|
271
|
+
Force also makes it incredibly easy to download Attachments:
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
attachment = client.query('select Id, Name, Body from Attachment').first
|
275
|
+
File.open(attachment.Name, 'wb') { |f| f.write(attachment.Body) }
|
276
|
+
```
|
277
|
+
|
278
|
+
### Custom Apex REST endpoints
|
279
|
+
|
280
|
+
You can use Force to interact with your custom REST endpoints, by using
|
281
|
+
`.get`, `.put`, `.patch`, `.post`, and `.delete`.
|
282
|
+
|
283
|
+
For example, if you had the following Apex REST endpoint on Salesforce:
|
284
|
+
|
285
|
+
```apex
|
286
|
+
@RestResource(urlMapping='/FieldCase/*')
|
287
|
+
global class RESTCaseController {
|
288
|
+
@HttpGet
|
289
|
+
global static List<Case> getOpenCases() {
|
290
|
+
String companyName = RestContext.request.params.get('company');
|
291
|
+
Account company = [ Select ID, Name, Email__c, BillingState from Account where Name = :companyName];
|
292
|
+
|
293
|
+
List<Case> cases = [SELECT Id, Subject, Status, OwnerId, Owner.Name from Case WHERE AccountId = :company.Id];
|
294
|
+
return cases;
|
295
|
+
}
|
296
|
+
}
|
297
|
+
```
|
298
|
+
|
299
|
+
...then you could query the cases using Force:
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
client.get '/services/apexrest/FieldCase', :company => 'GenePoint'
|
303
|
+
# => #<Force::Collection ...>
|
304
|
+
```
|
305
|
+
|
306
|
+
* * *
|
307
|
+
|
308
|
+
### Streaming
|
309
|
+
|
310
|
+
Force supports the [Streaming API](http://wiki.developerforce.com/page/Getting_Started_with_the_Force.com_Streaming_API), and makes implementing
|
311
|
+
pub/sub with Salesforce a trivial task:
|
312
|
+
|
313
|
+
```ruby
|
314
|
+
# Force uses faye as the underlying implementation for CometD.
|
315
|
+
require 'faye'
|
316
|
+
|
317
|
+
# Initialize a client with your username/password/oauth token/etc.
|
318
|
+
client = Force.new :username => 'foo',
|
319
|
+
:password => 'bar',
|
320
|
+
:security_token => 'security token'
|
321
|
+
:client_id => 'client_id',
|
322
|
+
:client_secret => 'client_secret'
|
323
|
+
|
324
|
+
# Create a PushTopic for subscribing to Account changes.
|
325
|
+
client.create! 'PushTopic', {
|
326
|
+
ApiVersion: '23.0',
|
327
|
+
Name: 'AllAccounts',
|
328
|
+
Description: 'All account records',
|
329
|
+
NotifyForOperations: 'All',
|
330
|
+
NotifyForFields: 'All',
|
331
|
+
Query: "select Id from Account"
|
332
|
+
}
|
333
|
+
|
334
|
+
EM.run {
|
335
|
+
# Subscribe to the PushTopic.
|
336
|
+
client.subscribe 'AllAccounts' do |message|
|
337
|
+
puts message.inspect
|
338
|
+
end
|
339
|
+
}
|
340
|
+
```
|
341
|
+
|
342
|
+
_See also: http://www.salesforce.com/us/developer/docs/api_streaming/index.htm_
|
343
|
+
|
344
|
+
* * *
|
345
|
+
|
346
|
+
### Caching
|
347
|
+
|
348
|
+
The gem supports easy caching of GET requests (e.g. queries):
|
349
|
+
|
350
|
+
```ruby
|
351
|
+
# rails example:
|
352
|
+
client = Force.new cache: Rails.cache
|
353
|
+
|
354
|
+
# or
|
355
|
+
Force.configure do |config|
|
356
|
+
config.cache = Rails.cache
|
357
|
+
end
|
358
|
+
```
|
359
|
+
|
360
|
+
If you enable caching, you can disable caching on a per-request basis by using
|
361
|
+
.without_caching:
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
client.without_caching do
|
365
|
+
client.query('select Id from Account')
|
366
|
+
end
|
367
|
+
```
|
368
|
+
|
369
|
+
### Logging / Debugging / Instrumenting
|
370
|
+
|
371
|
+
You can inspect what Force is sending/receiving by setting
|
372
|
+
`Force.log = true`.
|
373
|
+
|
374
|
+
```ruby
|
375
|
+
Force.log = true
|
376
|
+
client = Force.new.query('select Id, Name from Account')
|
377
|
+
```
|
378
|
+
|
379
|
+
Another awesome feature about force is that, because it is based on Faraday, you can insert your own middleware.
|
380
|
+
|
381
|
+
For example, if you were using Force in a Rails app, you can setup custom reporting to [Librato](https://github.com/librato/librato-rails) using ActiveSupport::Notifications:
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
client = Force.new do |builder|
|
385
|
+
builder.insert_after Force::Middleware::InstanceURL,
|
386
|
+
FaradayMiddleware::Instrumentation, name: 'request.salesforce'
|
387
|
+
end
|
388
|
+
```
|
389
|
+
|
390
|
+
#### config/initializers/notifications.rb
|
391
|
+
|
392
|
+
```ruby
|
393
|
+
ActiveSupport::Notifications.subscribe('request.salesforce') do |*args|
|
394
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
395
|
+
Librato.increment 'api.salesforce.request.total'
|
396
|
+
Librato.timing 'api.salesforce.request.time', event.duration
|
397
|
+
end
|
398
|
+
```
|
399
|
+
|
400
|
+
## Force.com Canvas
|
401
|
+
|
402
|
+
You can use Force to decode signed requests from Salesforce. See [the example app](https://gist.github.com/4052312).
|
403
|
+
|
404
|
+
## Tooling API
|
405
|
+
|
406
|
+
To use the [Tooling API](http://www.salesforce.com/us/developer/docs/api_toolingpre/api_tooling.pdf),
|
407
|
+
call `Force.tooling` instead of `Force.new`:
|
408
|
+
|
409
|
+
```ruby
|
410
|
+
client = Force.tooling(...)
|
411
|
+
```
|
412
|
+
|
413
|
+
---
|
414
|
+
|
415
|
+
## Contact
|
416
|
+
|
417
|
+
- Mattt Thompson <mattt@heroku.com>
|
418
|
+
|
419
|
+
## License
|
420
|
+
|
421
|
+
Force is available under the MIT license. See the LICENSE file for more info.
|