force 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +96 -0
  3. data/Gemfile +11 -0
  4. data/Gemfile.lock +107 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE +22 -0
  7. data/README.md +421 -0
  8. data/Rakefile +10 -0
  9. data/coverage/assets/0.7.1/application.css +1110 -0
  10. data/coverage/assets/0.7.1/application.js +626 -0
  11. data/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
  12. data/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
  13. data/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
  14. data/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
  15. data/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
  16. data/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
  17. data/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
  18. data/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
  19. data/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
  20. data/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
  21. data/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
  22. data/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
  23. data/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
  24. data/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
  25. data/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
  26. data/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
  27. data/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
  28. data/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
  29. data/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
  30. data/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
  31. data/coverage/assets/0.7.1/favicon_green.png +0 -0
  32. data/coverage/assets/0.7.1/favicon_red.png +0 -0
  33. data/coverage/assets/0.7.1/favicon_yellow.png +0 -0
  34. data/coverage/assets/0.7.1/loading.gif +0 -0
  35. data/coverage/assets/0.7.1/magnify.png +0 -0
  36. data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  37. data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  38. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  39. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  40. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  41. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  42. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  43. data/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  44. data/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
  45. data/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  46. data/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
  47. data/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
  48. data/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  49. data/coverage/index.html +19808 -0
  50. data/force.gemspec +27 -0
  51. data/lib/force.rb +74 -0
  52. data/lib/force/abstract_client.rb +9 -0
  53. data/lib/force/attachment.rb +21 -0
  54. data/lib/force/client.rb +3 -0
  55. data/lib/force/collection.rb +45 -0
  56. data/lib/force/concerns/api.rb +321 -0
  57. data/lib/force/concerns/authentication.rb +39 -0
  58. data/lib/force/concerns/base.rb +59 -0
  59. data/lib/force/concerns/caching.rb +24 -0
  60. data/lib/force/concerns/canvas.rb +10 -0
  61. data/lib/force/concerns/connection.rb +74 -0
  62. data/lib/force/concerns/picklists.rb +87 -0
  63. data/lib/force/concerns/streaming.rb +31 -0
  64. data/lib/force/concerns/verbs.rb +67 -0
  65. data/lib/force/config.rb +140 -0
  66. data/lib/force/data/client.rb +18 -0
  67. data/lib/force/mash.rb +66 -0
  68. data/lib/force/middleware.rb +27 -0
  69. data/lib/force/middleware/authentication.rb +73 -0
  70. data/lib/force/middleware/authentication/password.rb +17 -0
  71. data/lib/force/middleware/authentication/token.rb +15 -0
  72. data/lib/force/middleware/authorization.rb +15 -0
  73. data/lib/force/middleware/caching.rb +22 -0
  74. data/lib/force/middleware/gzip.rb +31 -0
  75. data/lib/force/middleware/instance_url.rb +14 -0
  76. data/lib/force/middleware/logger.rb +40 -0
  77. data/lib/force/middleware/mashify.rb +16 -0
  78. data/lib/force/middleware/multipart.rb +55 -0
  79. data/lib/force/middleware/raise_error.rb +25 -0
  80. data/lib/force/signed_request.rb +48 -0
  81. data/lib/force/sobject.rb +68 -0
  82. data/lib/force/tooling/client.rb +11 -0
  83. data/lib/force/upload_io.rb +20 -0
  84. data/lib/force/version.rb +3 -0
  85. data/spec/fixtures/auth_error_response.json +4 -0
  86. data/spec/fixtures/auth_success_response.json +7 -0
  87. data/spec/fixtures/blob.jpg +0 -0
  88. data/spec/fixtures/expired_session_response.json +6 -0
  89. data/spec/fixtures/reauth_success_response.json +7 -0
  90. data/spec/fixtures/refresh_error_response.json +4 -0
  91. data/spec/fixtures/refresh_success_response.json +7 -0
  92. data/spec/fixtures/services_data_success_response.json +12 -0
  93. data/spec/fixtures/sobject/create_success_response.json +5 -0
  94. data/spec/fixtures/sobject/delete_error_response.json +1 -0
  95. data/spec/fixtures/sobject/describe_sobjects_success_response.json +31 -0
  96. data/spec/fixtures/sobject/list_sobjects_success_response.json +31 -0
  97. data/spec/fixtures/sobject/org_query_response.json +11 -0
  98. data/spec/fixtures/sobject/query_aggregate_success_response.json +23 -0
  99. data/spec/fixtures/sobject/query_empty_response.json +5 -0
  100. data/spec/fixtures/sobject/query_error_response.json +6 -0
  101. data/spec/fixtures/sobject/query_paginated_first_page_response.json +14 -0
  102. data/spec/fixtures/sobject/query_paginated_last_page_response.json +13 -0
  103. data/spec/fixtures/sobject/query_success_response.json +38 -0
  104. data/spec/fixtures/sobject/recent_success_response.json +18 -0
  105. data/spec/fixtures/sobject/search_error_response.json +6 -0
  106. data/spec/fixtures/sobject/search_success_response.json +16 -0
  107. data/spec/fixtures/sobject/sobject_describe_error_response.json +6 -0
  108. data/spec/fixtures/sobject/sobject_describe_success_response.json +1429 -0
  109. data/spec/fixtures/sobject/sobject_find_error_response.json +6 -0
  110. data/spec/fixtures/sobject/sobject_find_success_response.json +29 -0
  111. data/spec/fixtures/sobject/upsert_created_success_response.json +5 -0
  112. data/spec/fixtures/sobject/upsert_error_response.json +6 -0
  113. data/spec/fixtures/sobject/upsert_multiple_error_response.json +4 -0
  114. data/spec/fixtures/sobject/upsert_updated_success_response.json +0 -0
  115. data/spec/fixtures/sobject/write_error_response.json +6 -0
  116. data/spec/integration/abstract_client_spec.rb +306 -0
  117. data/spec/integration/data/client_spec.rb +90 -0
  118. data/spec/spec_helper.rb +20 -0
  119. data/spec/support/client_integration.rb +45 -0
  120. data/spec/support/concerns.rb +18 -0
  121. data/spec/support/event_machine.rb +14 -0
  122. data/spec/support/fixture_helpers.rb +45 -0
  123. data/spec/support/matchers.rb +11 -0
  124. data/spec/support/middleware.rb +76 -0
  125. data/spec/support/mock_cache.rb +13 -0
  126. data/spec/unit/abstract_client_spec.rb +11 -0
  127. data/spec/unit/attachment_spec.rb +15 -0
  128. data/spec/unit/collection_spec.rb +52 -0
  129. data/spec/unit/concerns/api_spec.rb +244 -0
  130. data/spec/unit/concerns/authentication_spec.rb +98 -0
  131. data/spec/unit/concerns/base_spec.rb +42 -0
  132. data/spec/unit/concerns/caching_spec.rb +29 -0
  133. data/spec/unit/concerns/canvas_spec.rb +30 -0
  134. data/spec/unit/concerns/connection_spec.rb +22 -0
  135. data/spec/unit/config_spec.rb +99 -0
  136. data/spec/unit/data/client_spec.rb +10 -0
  137. data/spec/unit/mash_spec.rb +36 -0
  138. data/spec/unit/middleware/authentication/password_spec.rb +31 -0
  139. data/spec/unit/middleware/authentication/token_spec.rb +24 -0
  140. data/spec/unit/middleware/authentication_spec.rb +67 -0
  141. data/spec/unit/middleware/authorization_spec.rb +11 -0
  142. data/spec/unit/middleware/gzip_spec.rb +66 -0
  143. data/spec/unit/middleware/instance_url_spec.rb +24 -0
  144. data/spec/unit/middleware/logger_spec.rb +19 -0
  145. data/spec/unit/middleware/mashify_spec.rb +11 -0
  146. data/spec/unit/middleware/raise_error_spec.rb +32 -0
  147. data/spec/unit/signed_request_spec.rb +24 -0
  148. data/spec/unit/sobject_spec.rb +86 -0
  149. data/spec/unit/tooling/client_spec.rb +7 -0
  150. data/tmp/rspec_guard_result +1 -0
  151. 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
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in force.gemspec
4
+ gemspec
5
+
6
+ gem 'rake'
7
+ gem 'jruby-openssl', :platforms => :jruby
8
+
9
+ group :development do
10
+ gem 'guard-rspec'
11
+ end
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.