activeforce 1.5.0

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 (86) hide show
  1. data/Gemfile +15 -0
  2. data/Gemfile.lock +128 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.md +112 -0
  5. data/Rakefile +47 -0
  6. data/VERSION +1 -0
  7. data/activeforce.gemspec +151 -0
  8. data/app/models/salesforce/account.rb +4 -0
  9. data/app/models/salesforce/activity_history.rb +4 -0
  10. data/app/models/salesforce/approval.rb +4 -0
  11. data/app/models/salesforce/campaign.rb +4 -0
  12. data/app/models/salesforce/campaign_feed.rb +4 -0
  13. data/app/models/salesforce/campaign_member.rb +4 -0
  14. data/app/models/salesforce/case.rb +4 -0
  15. data/app/models/salesforce/case_comment.rb +4 -0
  16. data/app/models/salesforce/case_contact_role.rb +4 -0
  17. data/app/models/salesforce/case_feed.rb +4 -0
  18. data/app/models/salesforce/case_history.rb +4 -0
  19. data/app/models/salesforce/case_share.rb +4 -0
  20. data/app/models/salesforce/case_solution.rb +4 -0
  21. data/app/models/salesforce/case_status.rb +4 -0
  22. data/app/models/salesforce/case_team_member.rb +4 -0
  23. data/app/models/salesforce/community.rb +4 -0
  24. data/app/models/salesforce/contact.rb +4 -0
  25. data/app/models/salesforce/contact_feed.rb +4 -0
  26. data/app/models/salesforce/contact_history.rb +4 -0
  27. data/app/models/salesforce/contract.rb +4 -0
  28. data/app/models/salesforce/document.rb +4 -0
  29. data/app/models/salesforce/event.rb +4 -0
  30. data/app/models/salesforce/feed_item.rb +4 -0
  31. data/app/models/salesforce/group.rb +4 -0
  32. data/app/models/salesforce/group_member.rb +4 -0
  33. data/app/models/salesforce/idea.rb +4 -0
  34. data/app/models/salesforce/lead.rb +4 -0
  35. data/app/models/salesforce/lead_status.rb +4 -0
  36. data/app/models/salesforce/name.rb +4 -0
  37. data/app/models/salesforce/note.rb +4 -0
  38. data/app/models/salesforce/open_activity.rb +4 -0
  39. data/app/models/salesforce/opportunity.rb +4 -0
  40. data/app/models/salesforce/organization.rb +4 -0
  41. data/app/models/salesforce/partner.rb +4 -0
  42. data/app/models/salesforce/period.rb +4 -0
  43. data/app/models/salesforce/product2.rb +4 -0
  44. data/app/models/salesforce/product2_feed.rb +4 -0
  45. data/app/models/salesforce/profile.rb +4 -0
  46. data/app/models/salesforce/quote.rb +4 -0
  47. data/app/models/salesforce/solution.rb +4 -0
  48. data/app/models/salesforce/task.rb +4 -0
  49. data/app/models/salesforce/task_feed.rb +4 -0
  50. data/app/models/salesforce/task_priority.rb +4 -0
  51. data/app/models/salesforce/task_status.rb +4 -0
  52. data/app/models/salesforce/user.rb +4 -0
  53. data/app/models/salesforce/user_role.rb +4 -0
  54. data/app/models/salesforce/vote.rb +4 -0
  55. data/lib/activeforce.rb +29 -0
  56. data/lib/salesforce/attributes.rb +13 -0
  57. data/lib/salesforce/authentication.rb +25 -0
  58. data/lib/salesforce/base.rb +240 -0
  59. data/lib/salesforce/bulk/batch.rb +77 -0
  60. data/lib/salesforce/bulk/job.rb +103 -0
  61. data/lib/salesforce/bulk/operations.rb +25 -0
  62. data/lib/salesforce/bulk/update_job.rb +24 -0
  63. data/lib/salesforce/column.rb +98 -0
  64. data/lib/salesforce/columns.rb +60 -0
  65. data/lib/salesforce/config.rb +110 -0
  66. data/lib/salesforce/connection.rb +33 -0
  67. data/lib/salesforce/connection/async.rb +36 -0
  68. data/lib/salesforce/connection/conversion.rb +37 -0
  69. data/lib/salesforce/connection/http_methods.rb +72 -0
  70. data/lib/salesforce/connection/rest_api.rb +52 -0
  71. data/lib/salesforce/connection/soap_api.rb +74 -0
  72. data/lib/salesforce/engine.rb +6 -0
  73. data/lib/salesforce/errors.rb +33 -0
  74. data/test/salesforce/authentication_test.rb +52 -0
  75. data/test/salesforce/base_test.rb +440 -0
  76. data/test/salesforce/bulk/batch_test.rb +79 -0
  77. data/test/salesforce/bulk/update_job_test.rb +125 -0
  78. data/test/salesforce/column_test.rb +115 -0
  79. data/test/salesforce/config_test.rb +163 -0
  80. data/test/salesforce/connection/async_test.rb +138 -0
  81. data/test/salesforce/connection/http_methods_test.rb +242 -0
  82. data/test/salesforce/connection/rest_api_test.rb +61 -0
  83. data/test/salesforce/connection/soap_api_test.rb +148 -0
  84. data/test/salesforce/connection_test.rb +148 -0
  85. data/test/test_helper.rb +79 -0
  86. metadata +295 -0
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Account < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class ActivityHistory < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Approval < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Campaign < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CampaignFeed < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CampaignMember < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Case < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CaseComment < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CaseContactRole < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CaseFeed < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CaseHistory < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CaseShare < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CaseSolution < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CaseStatus < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class CaseTeamMember < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Community < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Contact < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class ContactFeed < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class ContactHistory < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Contract < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Document < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Event < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class FeedItem < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Group < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class GroupMember < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Idea < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Lead < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class LeadStatus < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Name < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Note < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class OpenActivity < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Opportunity < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Organization < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Partner < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Period < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Product2 < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Product2Feed < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Profile < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Quote < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Solution < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Task < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class TaskFeed < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class TaskPriority < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class TaskStatus < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class User < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class UserRole < Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Salesforce
2
+ class Vote < Base
3
+ end
4
+ end
@@ -0,0 +1,29 @@
1
+ require 'pp'
2
+ require 'blockenspiel'
3
+ require 'savon'
4
+ require 'rest-client'
5
+ require 'fastercsv'
6
+ require 'active_support/all'
7
+ require 'salesforce/config'
8
+ require 'salesforce/engine'
9
+ require 'salesforce/authentication'
10
+ require 'salesforce/attributes'
11
+ require 'salesforce/errors'
12
+ require 'salesforce/connection'
13
+ require 'salesforce/columns'
14
+ require 'salesforce/column'
15
+ require 'salesforce/bulk/operations'
16
+ require 'salesforce/base'
17
+ require 'salesforce/bulk/job'
18
+ require 'salesforce/bulk/update_job'
19
+ require 'salesforce/bulk/batch'
20
+
21
+ module Salesforce
22
+ def self.configure(&block)
23
+ Blockenspiel.invoke(block, Config.instance)
24
+ end
25
+
26
+ def self.connection
27
+ Connection
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ module Salesforce
2
+ module Attributes
3
+ extend ActiveSupport::Concern
4
+
5
+ module InstanceMethods
6
+ def assign_attributes!(hash)
7
+ hash.each do |key, value|
8
+ send("#{key}=", value) if respond_to?("#{key}=")
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Salesforce
2
+ class Authentication
3
+ cattr_accessor :username
4
+ cattr_accessor :password
5
+
6
+ def self.session_id
7
+ raise InvalidCredentials.new("No credentials provided.") if Config.username.blank? || Config.password.blank?
8
+ Config.session_id || generate_new_session_id
9
+ end
10
+
11
+ def self.generate_new_session_id
12
+ result = Connection.login
13
+ Config.instance.soap_endpoint_url result[:server_url]
14
+ Config.instance.session_id result[:session_id]
15
+ Config.instance.server_instance URI.parse(result[:server_url]).host.split("-").first
16
+ Config.instance.user_id result[:user_id]
17
+ Config.session_id
18
+ end
19
+
20
+ def self.logout
21
+ Config.instance.session_id nil
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,240 @@
1
+ module Salesforce
2
+ class Base
3
+ class_attribute :custom_object, :custom_table_name, :cached_columns, :attributes
4
+ include ::Salesforce::Bulk::Operations
5
+
6
+ self.custom_object = false
7
+
8
+ def initialize(attrs = {})
9
+ self.class.columns
10
+ assign_attributes(attrs)
11
+ end
12
+
13
+ def assign_attributes(attrs)
14
+ attrs.delete("attributes")
15
+ attrs.each do |k,v|
16
+ send("#{k}=", v)
17
+ end
18
+ end
19
+
20
+ def self.table_name
21
+ return self.custom_table_name if self.custom_table_name
22
+ name.demodulize + (self.custom_object ? "__c" : '')
23
+ end
24
+
25
+ def self.columns
26
+ self.cached_columns ||= begin
27
+ cols = Columns.new(table_name)
28
+ cols.all.each do |col|
29
+ attr_reader col.name
30
+
31
+ define_method "#{col.original_name}=" do |value|
32
+ instance_variable_set(:"@#{col.name}", Column.typecast(col.type, value))
33
+ end
34
+ end
35
+
36
+ cols.editable.each do |col|
37
+ define_method "#{col.name}=" do |value|
38
+ if (new_record? && col.createable?) || (!new_record? && col.updateable?)
39
+ instance_variable_set(:"@#{col.name}", Column.typecast(col.type, value))
40
+ else
41
+ raise ArgumentError.new("#{self.class.table_name}##{col.name} is not editable.")
42
+ end
43
+ end
44
+ end
45
+ cols
46
+ end
47
+ end
48
+
49
+ ## Attributes
50
+
51
+ def attributes
52
+ self.class.columns.names.inject(ActiveSupport::HashWithIndifferentAccess.new) do |hash,name|
53
+ hash[name] = self.send(name)
54
+ hash
55
+ end
56
+ end
57
+
58
+ def createable_attributes
59
+ self.class.columns.createable.inject(ActiveSupport::HashWithIndifferentAccess.new) do |hash,col|
60
+ value = self.send(col.name)
61
+ hash[col.original_name] = value unless value.nil?
62
+ hash
63
+ end
64
+ end
65
+
66
+ def updateable_attributes
67
+ self.class.columns.updateable.inject(ActiveSupport::HashWithIndifferentAccess.new) do |hash,col|
68
+ value = self.send(col.name)
69
+ hash[col.original_name] = value unless value.nil?
70
+ hash
71
+ end
72
+ end
73
+
74
+ def attributes=(attrs)
75
+ assign_attributes(attrs)
76
+ end
77
+
78
+ ## Finders
79
+
80
+ def self.find(*args)
81
+ if args.first == :all
82
+ find_all(*args)
83
+ else
84
+ find_by_id(*args)
85
+ end
86
+ end
87
+
88
+ def self.find_by_id(object_id)
89
+ with_invalid_column_handling do
90
+ to_object(connection(:find_object_by_id, table_name, object_id, columns.soql_selector))
91
+ end
92
+ end
93
+
94
+ def self.find_all(*args)
95
+ with_invalid_column_handling do
96
+ options = args.extract_options!
97
+ connection(:soql, query_string(options)).map { |result| to_object(result) }
98
+ end
99
+ end
100
+
101
+ def self.all; find_all; end
102
+
103
+ def self.find_by_column(column, value)
104
+ connection(:soql, "#{select_clause} FROM #{table_name} WHERE #{column.original_name}=#{Column.to_soql_value(value)}").map { |result| to_object(result) }
105
+ end
106
+
107
+ def self.select_values(options)
108
+ connection(:soql, query_string(options)).map do |result|
109
+ {}.tap do |hash|
110
+ Array.wrap(options[:select] || columns.names).each do |col|
111
+ column = columns.find_by_name(col)
112
+ hash[col] = Column.typecast(column.type, result[column.original_name])
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ def self.with_invalid_column_handling
119
+ _count = 0
120
+ begin
121
+ yield
122
+ rescue InvalidRequest => e
123
+ if e.error_code == 'INVALID_FIELD' && _count == 0
124
+ clear_cached_columns!
125
+ _count += 1
126
+ retry
127
+ else
128
+ raise e
129
+ end
130
+ end
131
+ end
132
+
133
+ def self.clear_cached_columns!
134
+ self.cached_columns = nil
135
+ end
136
+
137
+ def save!
138
+ raise_if_destroyed
139
+ new_record? ? create : update
140
+ end
141
+
142
+ def create
143
+ raise_if_destroyed
144
+ self.Id = self.class.connection(:create, self.class.table_name, createable_attributes)
145
+ end
146
+
147
+ def update
148
+ self.class.with_invalid_column_handling do
149
+ raise_if_destroyed
150
+ self.class.connection(:update, self.class.table_name, self.id, updateable_attributes)
151
+ end
152
+ end
153
+
154
+ def destroy
155
+ raise_if_destroyed
156
+ self.class.connection(:destroy, self.class.table_name, self.id) unless new_record?
157
+ @destroyed = true
158
+ end
159
+
160
+ def destroyed?
161
+ @destroyed || false
162
+ end
163
+
164
+ def raise_if_destroyed
165
+ raise ObjectDestroyed.new if destroyed?
166
+ end
167
+
168
+ def new_record?
169
+ self.id.blank?
170
+ end
171
+
172
+ def self.select_clause(select = nil)
173
+ "SELECT #{selector(select)}"
174
+ end
175
+
176
+ def self.selector(select = nil)
177
+ select ||= :all
178
+ return columns.soql_selector if select == :all
179
+ Array.wrap(select).map { |var| columns.find_by_name(var).original_name }.join(',')
180
+ end
181
+
182
+ def self.query_string(options)
183
+ str = [].tap do |string|
184
+ string << "SELECT #{selector(options[:select])} FROM #{table_name}"
185
+ string << "WHERE #{soql_clause(options[:conditions])}" if options[:conditions]
186
+ string << "LIMIT #{soql_clause(options[:limit])}" if options[:limit]
187
+ string << "WITH #{soql_clause(options[:with])}" if options[:with]
188
+ string << "GROUP BY #{soql_clause(options[:group_by])}" if options[:group_by]
189
+ string << "HAVING #{soql_clause(options[:having])}" if options[:having]
190
+ string << "ORDER BY #{soql_clause(options[:order])}" if options[:order]
191
+ end.join(' ')
192
+ sanitized_soql(str, options)
193
+ end
194
+
195
+ def self.soql_clause(sub_clause)
196
+ return sub_clause if sub_clause.is_a?(String)
197
+ Array.wrap(sub_clause).map { |col| columns.find_by_name(col).original_name }.join(',')
198
+ end
199
+
200
+ def self.sanitized_soql(string, values = nil)
201
+ values ||= {}
202
+ # Substitute columns
203
+ columns.each do |col|
204
+ string.gsub!(":#{col.name}", col.original_name)
205
+ end
206
+
207
+ # Substitute Values
208
+ values.each do |key, value|
209
+ next if [ :select, :conditions, :limit, :with, :group_by, :having, :order ].include?(key.to_sym)
210
+ string.gsub!(":#{key}", Column.to_soql_value(value))
211
+ end
212
+ string
213
+ end
214
+
215
+
216
+ def self.connection(method, *args)
217
+ columns
218
+ Connection.send method, *args
219
+ end
220
+
221
+ def self.method_missing(method_name, *args)
222
+ if method_name.to_s =~ /^find_by_([_a-zA-Z]\w*)$/ && column = columns.find { |column| column.name.to_s == $1 }
223
+ self.find_by_column(column, *args)
224
+ else
225
+ super
226
+ end
227
+ end
228
+
229
+ def self.to_object(result)
230
+ result.delete("attributes")
231
+ new.tap do |record|
232
+ result.each do |k, v|
233
+ if record.respond_to?(:"#{k}=")
234
+ record.send("#{k}=", v)
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end