openstax_active_force 1.0.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 (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.mailmap +3 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.md +98 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +174 -0
  10. data/Rakefile +6 -0
  11. data/active_force.gemspec +30 -0
  12. data/lib/active_attr/dirty.rb +33 -0
  13. data/lib/active_force/active_query.rb +155 -0
  14. data/lib/active_force/association/association.rb +50 -0
  15. data/lib/active_force/association/belongs_to_association.rb +26 -0
  16. data/lib/active_force/association/eager_load_projection_builder.rb +60 -0
  17. data/lib/active_force/association/has_many_association.rb +33 -0
  18. data/lib/active_force/association/relation_model_builder.rb +70 -0
  19. data/lib/active_force/association.rb +28 -0
  20. data/lib/active_force/attribute.rb +30 -0
  21. data/lib/active_force/mapping.rb +78 -0
  22. data/lib/active_force/query.rb +110 -0
  23. data/lib/active_force/sobject.rb +210 -0
  24. data/lib/active_force/standard_types.rb +357 -0
  25. data/lib/active_force/table.rb +37 -0
  26. data/lib/active_force/version.rb +3 -0
  27. data/lib/active_force.rb +13 -0
  28. data/lib/generators/active_force/model/USAGE +8 -0
  29. data/lib/generators/active_force/model/model_generator.rb +62 -0
  30. data/lib/generators/active_force/model/templates/model.rb.erb +5 -0
  31. data/spec/active_force/active_query_spec.rb +178 -0
  32. data/spec/active_force/association/relation_model_builder_spec.rb +62 -0
  33. data/spec/active_force/association_spec.rb +157 -0
  34. data/spec/active_force/attribute_spec.rb +27 -0
  35. data/spec/active_force/callbacks_spec.rb +20 -0
  36. data/spec/active_force/mapping_spec.rb +18 -0
  37. data/spec/active_force/query_spec.rb +126 -0
  38. data/spec/active_force/sobject/includes_spec.rb +290 -0
  39. data/spec/active_force/sobject/table_name_spec.rb +27 -0
  40. data/spec/active_force/sobject_spec.rb +398 -0
  41. data/spec/active_force/table_spec.rb +25 -0
  42. data/spec/active_force_spec.rb +7 -0
  43. data/spec/fixtures/sobject/single_sobject_hash.yml +26 -0
  44. data/spec/spec_helper.rb +16 -0
  45. data/spec/support/fixture_helpers.rb +45 -0
  46. data/spec/support/restforce_factories.rb +9 -0
  47. data/spec/support/sobjects.rb +97 -0
  48. data/spec/support/whizbang.rb +30 -0
  49. metadata +196 -0
@@ -0,0 +1,357 @@
1
+ module StandardTypes
2
+ STANDARD_TYPES = %w[
3
+ Account
4
+ Asset
5
+ AssetFeed
6
+ AssetTag
7
+ AssignmentRule
8
+ AsyncApexJob
9
+ AttachedContentDocument
10
+ Attachment
11
+ AuthSession
12
+ Bookmark
13
+ BrandTemplate
14
+ BusinessHours
15
+ BusinessProcess
16
+ CallCenter
17
+ Campaign
18
+ CampaignFeed
19
+ CampaignMember
20
+ CampaignMemberStatus
21
+ CampaignOwnerSharingRule
22
+ Campaign
23
+ CampaignShare
24
+ CampaignTag
25
+ Case
26
+ CaseArticle
27
+ CaseComment
28
+ CaseContactRole
29
+ CaseFeed
30
+ CaseHistory
31
+ CaseMilestone
32
+ CaseOwnerSharingRule
33
+ CaseShare
34
+ CaseSolution
35
+ Solution
36
+ CaseStatus
37
+ CaseTag
38
+ CaseTeamMember
39
+ CaseTeamRole
40
+ CaseTeamTemplate
41
+ CaseTeamTemplateMember
42
+ CaseTeamTemplateRecord
43
+ CategoryData
44
+ CategoryNode
45
+ CategoryNodeLocalization
46
+ ChatterActivity
47
+ ChatterAnswersActivity
48
+ ChatterAnswersReputationLevel
49
+ ChatterAnswersReputationLevel
50
+ ChatterConversation
51
+ ChatterConversationMember
52
+ ChatterMessage
53
+ CollaborationGroup
54
+ CollaborationGroupFeed
55
+ CollaborationGroupMember
56
+ CollaborationGroupMemberRequest
57
+ CollaborationInvitation
58
+ CombinedAttachment
59
+ Community
60
+ Contact
61
+ ContactFeed
62
+ ContactHistory
63
+ ContactOwnerSharingRule
64
+ ContactShare
65
+ ContactTag
66
+ ContentDocument
67
+ ContentDocumentFeed
68
+ ContentDocumentHistory
69
+ ContentDocumentLink
70
+ ContentVersion
71
+ ContentVersionHistory
72
+ ContentWorkspace
73
+ ContentWorkspaceDoc
74
+ Contract
75
+ ContractContactRole
76
+ ContractFeed
77
+ ContractHistory
78
+ ContractLineItem
79
+ ContractLineItemHistory
80
+ ContractStatus
81
+ ContractTag
82
+ CronTrigger
83
+ CronJobDetail
84
+ CurrencyType
85
+ CustomBrand
86
+ CustomBrandAsset
87
+ CustomPermission
88
+ DandBCompany
89
+ Dashboard
90
+ DashboardComponent
91
+ DashboardComponentFeed
92
+ DashboardFeed
93
+ DashboardTag
94
+ DatacloudCompany
95
+ DatacloudContact
96
+ DatacloudDandBCompany
97
+ DatacloudOwnedEntity
98
+ DatacloudPurchaseUsage
99
+ DatacloudSocialHandle
100
+ DatedConversionRate
101
+ DeclinedEventRelation
102
+ Division
103
+ DivisionLocalization
104
+ Document
105
+ DocumentAttachmentMap
106
+ DocumentTag
107
+ EmailMessage
108
+ EmailServicesAddress
109
+ EmailServicesFunction
110
+ EmailStatus
111
+ EmailTemplate
112
+ Entitlement
113
+ EntitlementContact
114
+ EntitlementFeed
115
+ EntitlementHistory
116
+ EntitlementTemplate
117
+ EntityHistory
118
+ EntitySubscription
119
+ EnvironmentHubMember
120
+ Event
121
+ EventFeed
122
+ EventRelation
123
+ EventTag
124
+ EventWhoRelation
125
+ EventRelation
126
+ ExternalDataSource
127
+ ExternalDataUserAuth
128
+ FeedComment
129
+ FeedItem
130
+ FeedLike
131
+ FeedPollChoice
132
+ FeedPollVote
133
+ FeedPost
134
+ FeedTrackedChange
135
+ FieldPermissions
136
+ FiscalYearSettings
137
+ Folder
138
+ ForecastingAdjustment
139
+ ForecastingFact
140
+ ForecastingItem
141
+ ForecastingQuota
142
+ ForecastShare
143
+ Group
144
+ GroupMember
145
+ HashtagDefinition
146
+ Holiday
147
+ Idea
148
+ IdeaComment
149
+ IdeaTheme
150
+ KnowledgeableUser
151
+ KnowledgeArticle
152
+ KnowledgeArticleVersion
153
+ KnowledgeArticleVersionHistory
154
+ KnowledgeArticleViewStat
155
+ KnowledgeArticleVoteStat
156
+ Lead
157
+ LeadFeed
158
+ LeadHistory
159
+ LeadOwnerSharingRule
160
+ LeadShare
161
+ LeadStatus
162
+ LeadTag
163
+ LimitAllocationPerApp
164
+ LineitemOverride
165
+ LoginHistory
166
+ MailmergeTemplate
167
+ MilestoneType
168
+ Name
169
+ Network
170
+ NetworkActivityAudit
171
+ NetworkMember
172
+ NetworkMemberGroup
173
+ NetworkModeration
174
+ NewsFeed
175
+ Note
176
+ NoteAndAttachment
177
+ NoteTag
178
+ ObjectPermissions
179
+ ObjectTerritory2AssignmentRule
180
+ ObjectTerritory2AssignmentRuleItem
181
+ ObjectTerritory2Association
182
+ OpenActivity
183
+ Opportunity
184
+ OpportunityCompetitor
185
+ OpportunityContactRole
186
+ OpportunityFeed
187
+ OpportunityFieldHistory
188
+ OpportunityHistory
189
+ OpportunityLineItem
190
+ OpportunityLineItemSchedule
191
+ OpportunityOverride
192
+ OpportunityOwnerSharingRule
193
+ OpportunityPartner
194
+ OpportunityShare
195
+ OpportunitySplit
196
+ OpportunitySplitType
197
+ OpportunityStage
198
+ OpportunityTag
199
+ OpportunityTeamMember
200
+ Organization
201
+ OrgWideEmailAddress
202
+ OwnedContentDocument
203
+ PackageLicense
204
+ Partner
205
+ PartnerNetworkConnection
206
+ PartnerNetworkRecordConnection
207
+ PartnerRole
208
+ Period
209
+ PermissionSet
210
+ PermissionSetAssignment
211
+ Pricebook2
212
+ Pricebook2History
213
+ PricebookEntry
214
+ ProcessDefinition
215
+ ProcessInstance
216
+ ProcessInstanceHistory
217
+ ProcessInstanceNode
218
+ ProcessInstanceStep
219
+ ProcessInstanceWorkitem
220
+ ProcessNode
221
+ Product2
222
+ Product2Feed
223
+ ProductEntitlementTemplate
224
+ Profile
225
+ ProfileSkill
226
+ ProfileSkillEndorsement
227
+ ProfileSkillEndorsementHistory
228
+ ProfileSkillFeed
229
+ ProfileSkillHistory
230
+ ProfileSkillShare
231
+ ProfileSkillUser
232
+ ProfileSkillUserHistory
233
+ PushTopic
234
+ QuantityForecast
235
+ QuantityForecastHistory
236
+ Question
237
+ QuestionDataCategorySelection
238
+ QuestionReportAbuse
239
+ QuestionSubscription
240
+ QueueSobject
241
+ Quote
242
+ QuoteDocument
243
+ QuoteLineItem
244
+ RecentlyViewed
245
+ RecordType
246
+ RecordTypeLocalization
247
+ Reply
248
+ ReplyReportAbuse
249
+ Report
250
+ ReportFeed
251
+ ReportTag
252
+ RevenueForecast
253
+ RevenueForecastHistory
254
+ RuleTerritory2Association
255
+ Scontrol
256
+ ScontrolLocalization
257
+ SelfServiceUser
258
+ ServiceContract
259
+ ServiceContractFeed
260
+ ServiceContractHistory
261
+ ServiceContractOwnerSharingRule
262
+ ServiceContractShare
263
+ SetupEntityAccess
264
+ SignupRequest
265
+ Site
266
+ SiteHistory
267
+ SlaProcess
268
+ Solution
269
+ SolutionFeed
270
+ SolutionHistory
271
+ SolutionStatus
272
+ SolutionTag
273
+ StaticResource
274
+ StreamingChannel
275
+ TagDefinition
276
+ Task
277
+ TaskFeed
278
+ TaskPriority
279
+ TaskRelation
280
+ TaskStatus
281
+ TaskTag
282
+ TaskWhoRelation
283
+ TaskRelation
284
+ Territory
285
+ Territory2
286
+ Territory2Model
287
+ Territory2ModelHistory
288
+ Territory2Type
289
+ Topic
290
+ TopicAssignment
291
+ TopicFeed
292
+ UndecidedEventRelation
293
+ User
294
+ UserAccountTeamMember
295
+ UserConfigTransferButton
296
+ UserConfigTransferSkill
297
+ UserFeed
298
+ UserLicense
299
+ UserLogin
300
+ UserMembershipSharingRule
301
+ UserPackageLicense
302
+ UserPreference
303
+ UserProfile
304
+ UserProfileFeed
305
+ UserRecordAccess
306
+ UserRole
307
+ UserShare
308
+ UserTeamMember
309
+ UserTerritory
310
+ UserTerritory2Association
311
+ Vote
312
+ WebLink
313
+ WebLinkLocalization
314
+ WorkAccess
315
+ WorkAccessShare
316
+ WorkBadge
317
+ WorkBadgeDefinition
318
+ WorkBadgeDefinitionHistory
319
+ WorkBadgeDefinitionShare
320
+ WorkCoaching
321
+ WorkCoachingFeed
322
+ WorkCoachingHistory
323
+ WorkCoachingShare
324
+ WorkFeedback
325
+ WorkFeedbackQuestion
326
+ WorkFeedbackQuestionHistory
327
+ WorkFeedbackQuestionSet
328
+ WorkFeedbackQuestionSetShare
329
+ WorkFeedbackQuestionShare
330
+ WorkFeedbackRequest
331
+ WorkFeedbackRequestHistory
332
+ WorkFeedbackRequestShare
333
+ WorkFeedbackShare
334
+ WorkGoal
335
+ WorkGoalCollaborator
336
+ WorkGoalCollaboratorHistory
337
+ WorkGoalFeed
338
+ WorkGoalHistory
339
+ WorkGoalLink
340
+ WorkGoalShare
341
+ WorkPerformanceCycle
342
+ WorkPerformanceCycleFeed
343
+ WorkPerformanceCycleHistory
344
+ WorkPerformanceCycleShare
345
+ WorkReward
346
+ WorkRewardFund
347
+ WorkRewardFundHistory
348
+ WorkRewardFundShare
349
+ WorkRewardFundType
350
+ WorkRewardFundTypeHistory
351
+ WorkRewardFundTypeShare
352
+ WorkRewardHistory
353
+ WorkRewardShare
354
+ WorkThanks
355
+ WorkThanksShare
356
+ ]
357
+ end
@@ -0,0 +1,37 @@
1
+ require 'active_force/standard_types'
2
+
3
+ module ActiveForce
4
+ class Table
5
+
6
+ def initialize klass
7
+ @klass = klass.to_s
8
+ end
9
+
10
+ def table_name name = nil
11
+ @name = name || @name || pick_table_name
12
+ end
13
+
14
+ def name
15
+ @name ||= pick_table_name
16
+ end
17
+
18
+ def custom_table?
19
+ !StandardTypes::STANDARD_TYPES.include?(name_without_namespace)
20
+ end
21
+
22
+ private
23
+
24
+ def pick_table_name
25
+ if custom_table?
26
+ "#{ name_without_namespace }__c"
27
+ else
28
+ name_without_namespace
29
+ end
30
+ end
31
+
32
+ def name_without_namespace
33
+ @klass.split('::').last
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveForce
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'active_force/version'
2
+ require 'active_force/sobject'
3
+ require 'active_force/query'
4
+
5
+ module ActiveForce
6
+
7
+ class << self
8
+ attr_accessor :sfdc_client
9
+ end
10
+
11
+ self.sfdc_client = Restforce.new
12
+
13
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generate a model to interact with Sales Force.
3
+
4
+ Example:
5
+ rails generate active_force_model Account
6
+
7
+ This will create:
8
+ app/models/account.rb
@@ -0,0 +1,62 @@
1
+ module ActiveForce
2
+ class ModelGenerator < Rails::Generators::NamedBase
3
+ desc 'This generator loads the table fields from SFDC and generates the fields for the SObject with a more ruby names'
4
+
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def create_model_file
8
+ @table_name = file_name.capitalize
9
+ @class_name = @table_name.gsub '__c', ''
10
+ template "model.rb.erb", "app/models/#{@class_name.downcase}.rb" if table_exists?
11
+ end
12
+
13
+ protected
14
+
15
+ Attribute = Struct.new :field, :column
16
+
17
+ def attributes
18
+ @attributes ||= sfdc_columns.map do |column|
19
+ Attribute.new column_to_field(column), column
20
+ end
21
+ @attributes - [:id]
22
+ end
23
+
24
+ def sfdc_columns
25
+ @columns ||= ActiveForce::SObject.sfdc_client.describe(@table_name).fields.map do |field|
26
+ field.name
27
+ end
28
+ end
29
+
30
+ def table_exists?
31
+ !! sfdc_columns
32
+ rescue Faraday::Error::ResourceNotFound
33
+ puts "The specified table name is not found. Be sure to append __c if it's custom"
34
+ end
35
+
36
+ def column_to_field column
37
+ column.underscore.gsub("__c", "").to_sym
38
+ end
39
+
40
+ def attribute_line attribute
41
+ "field :#{ attribute.field },#{ space_justify attribute.field } from: '#{ attribute.column }'"
42
+ end
43
+
44
+ def space_justify field_name
45
+ longest_field = attributes.map { |attr| attr.field.length } .max
46
+ justify_count = longest_field - field_name.length
47
+ " " * justify_count
48
+ end
49
+
50
+
51
+ class String
52
+ def underscore
53
+ self.gsub(/::/, '/').
54
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
55
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
56
+ tr("-", "_").
57
+ downcase
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,5 @@
1
+ class <%= @class_name %> < ActiveForce::SObject
2
+ <% attributes.each do |attribute| -%>
3
+ <%= attribute_line attribute %>
4
+ <% end -%>
5
+ end
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce::ActiveQuery do
4
+ let(:sobject) do
5
+ double("sobject", {
6
+ table_name: "table_name",
7
+ fields: [],
8
+ mappings: mappings
9
+ })
10
+ end
11
+ let(:mappings){ { id: "Id", field: "Field__c", other_field: "Other_Field" } }
12
+ let(:client){ double("client") }
13
+ let(:active_query){ ActiveForce::ActiveQuery.new(sobject) }
14
+
15
+ before do
16
+ allow(active_query).to receive(:sfdc_client).and_return client
17
+ allow(active_query).to receive(:build).and_return Object.new
18
+ end
19
+
20
+ describe "to_a" do
21
+ before do
22
+ expect(client).to receive(:query)
23
+ end
24
+
25
+ it "should return an array of objects" do
26
+ result = active_query.where("Text_Label = 'foo'").to_a
27
+ expect(result).to be_a Array
28
+ end
29
+
30
+ it "should allow to chain query methods" do
31
+ result = active_query.where("Text_Label = 'foo'").where("Checkbox_Label = true").to_a
32
+ expect(result).to be_a Array
33
+ end
34
+ end
35
+
36
+ describe "select only some field using mappings" do
37
+ it "should return a query only with selected field" do
38
+ active_query.select(:field)
39
+ expect(active_query.to_s).to eq("SELECT Field__c FROM table_name")
40
+ end
41
+ end
42
+
43
+ describe "condition mapping" do
44
+ it "maps conditions for a .where" do
45
+ active_query.where(field: 123)
46
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
47
+ end
48
+
49
+ it 'transforms an array to a WHERE/IN clause' do
50
+ active_query.where(field: ['foo', 'bar'])
51
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c IN ('foo','bar'))")
52
+ end
53
+
54
+ it "encloses the value in quotes if it's a string" do
55
+ active_query.where field: "hello"
56
+ expect(active_query.to_s).to end_with("(Field__c = 'hello')")
57
+ end
58
+
59
+ it "puts NULL when a field is set as nil" do
60
+ active_query.where field: nil
61
+ expect(active_query.to_s).to end_with("(Field__c = NULL)")
62
+ end
63
+
64
+ describe 'bind parameters' do
65
+ let(:mappings) do
66
+ super().merge({
67
+ other_field: 'Other_Field__c',
68
+ name: 'Name'
69
+ })
70
+ end
71
+
72
+ it 'accepts bind parameters' do
73
+ active_query.where('Field__c = ?', 123)
74
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
75
+ end
76
+
77
+ it 'accepts nil bind parameters' do
78
+ active_query.where('Field__c = ?', nil)
79
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = NULL)")
80
+ end
81
+
82
+ it 'accepts multiple bind parameters' do
83
+ active_query.where('Field__c = ? AND Other_Field__c = ? AND Name = ?', 123, 321, 'Bob')
84
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
85
+ end
86
+
87
+ it 'complains when there given an incorrect number of bind parameters' do
88
+ expect{
89
+ active_query.where('Field__c = ? AND Other_Field__c = ? AND Name = ?', 123, 321)
90
+ }.to raise_error(ActiveForce::PreparedStatementInvalid, 'wrong number of bind variables (2 for 3)')
91
+ end
92
+
93
+ context 'named bind parameters' do
94
+ it 'accepts bind parameters' do
95
+ active_query.where('Field__c = :field', field: 123)
96
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
97
+ end
98
+
99
+ it 'accepts nil bind parameters' do
100
+ active_query.where('Field__c = :field', field: nil)
101
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = NULL)")
102
+ end
103
+
104
+ it 'accepts multiple bind parameters' do
105
+ active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', field: 123, other_field: 321, name: 'Bob')
106
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
107
+ end
108
+
109
+ it 'accepts multiple bind parameters orderless' do
110
+ active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', name: 'Bob', other_field: 321, field: 123)
111
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
112
+ end
113
+
114
+ it 'complains when there given an incorrect number of bind parameters' do
115
+ expect{
116
+ active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', field: 123, other_field: 321)
117
+ }.to raise_error(ActiveForce::PreparedStatementInvalid, 'missing value for :name in Field__c = :field AND Other_Field__c = :other_field AND Name = :name')
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe "#find_by" do
124
+ it "should query the client, with the SFDC field names and correctly enclosed values" do
125
+ expect(client).to receive :query
126
+ active_query.find_by field: 123
127
+ expect(active_query.to_s).to eq "SELECT Id FROM table_name WHERE (Field__c = 123) LIMIT 1"
128
+ end
129
+ end
130
+
131
+ describe "responding as an enumerable" do
132
+ before do
133
+ expect(active_query).to receive(:to_a).and_return([])
134
+ end
135
+
136
+ it "should call to_a when receiving each" do
137
+ active_query.each {}
138
+ end
139
+
140
+ it "should call to_a when receiving map" do
141
+ active_query.map {}
142
+ end
143
+ end
144
+
145
+ describe "prevent SOQL injection attacks" do
146
+ let(:mappings){ { quote_field: "QuoteField", backslash_field: "Backslash_Field__c", number_field: "NumberField" } }
147
+ let(:quote_input){ "' OR Id!=NULL OR Id='" }
148
+ let(:backslash_input){ "\\" }
149
+ let(:number_input){ 123 }
150
+ let(:expected_query){ "SELECT Id FROM table_name WHERE (Backslash_Field__c = '\\\\' AND NumberField = 123 AND QuoteField = ''' OR Id!=NULL OR Id=''')" }
151
+
152
+ it 'escapes quotes and backslashes in bind parameters' do
153
+ active_query.where('Backslash_Field__c = :backslash_field AND NumberField = :number_field AND QuoteField = :quote_field', number_field: number_input, backslash_field: backslash_input, quote_field: quote_input)
154
+ expect(active_query.to_s).to eq(expected_query)
155
+ end
156
+
157
+ it 'escapes quotes and backslashes in named bind parameters' do
158
+ active_query.where('Backslash_Field__c = ? AND NumberField = ? AND QuoteField = ?', backslash_input, number_input, quote_input)
159
+ expect(active_query.to_s).to eq(expected_query)
160
+ end
161
+
162
+ it 'escapes quotes and backslashes in hash conditions' do
163
+ active_query.where(backslash_field: backslash_input, number_field: number_input, quote_field: quote_input)
164
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Backslash_Field__c = '\\\\') AND (NumberField = 123) AND (QuoteField = ''' OR Id!=NULL OR Id=''')")
165
+ end
166
+ end
167
+
168
+ describe '#none' do
169
+ it 'returns a query with a where clause that is impossible to satisfy' do
170
+ expect(active_query.none.to_s).to eq "SELECT Id FROM table_name WHERE (Id = '111111111111111111') AND (Id = '000000000000000000')"
171
+ end
172
+
173
+ it 'does not query the API' do
174
+ expect(client).to_not receive :query
175
+ active_query.none.to_a
176
+ end
177
+ end
178
+ end