force 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.