active_force 0.16.0 → 0.17.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7691f9f6f82d893a6aafdf29cfc6228ab2fe31988fafcf5be95070347a0286de
4
- data.tar.gz: 4d0da25e5e12f9014407dba1871953a0b05f58227188d53130eba27dd537aa4d
3
+ metadata.gz: 741f33a6c27d6bc11eaaa7655c2a7fb2ecc772f93d298e608866840b82e94321
4
+ data.tar.gz: 465abe2c572e35dc61817ab1bf8ac1d6d0e35e6f26eb2e933af3ed1781c2c68a
5
5
  SHA512:
6
- metadata.gz: 2e8cf96c34eb81e3e4236455538bc462de07d02f7149ab064125d4c86f52a96c7e61e98ff91b9fe48e3b5b6d79d88ded4bb47966800f021e153a970adac802b4
7
- data.tar.gz: 6b7edaa7cf4161921b1d42c3a26fe896781bd3198ce981b2693852f3fe3ff6f1690e27e61ec781761fbe06d10634fc720013f1b2f3a266d3de6a189dd37af2a5
6
+ metadata.gz: 376ab891dc3a0bf92f030dd3b756aa900c8091bfd42b3424ef1c832ecc2ac14384785f96eeadc20764f22ee02315753cba10c4b0aec067294e2175a09754dc60
7
+ data.tar.gz: f60cf65275aaee5e96ebb173cc0718cbc663b3cced951bccf8b417df1029055606be56bdf82617e689130d68687b8ce8f8227acab83314c54715e95e6ca28629
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Not released
4
4
 
5
+ ## 0.17.0
6
+
7
+ - Fix bug with has_many queries due to query method chaining mutating in-place (https://github.com/Beyond-Finance/active_force/pull/10)
8
+
5
9
  ## 0.16.0
6
10
 
7
11
  - Fix `default` in models when default value is overridden by the same value, it is still sent to salesforce (https://github.com/Beyond-Finance/active_force/pull/61)
@@ -42,36 +42,34 @@ module ActiveForce
42
42
  alias_method :all, :to_a
43
43
 
44
44
  def count
45
- super
46
- sfdc_client.query(to_s).first.expr0
45
+ sfdc_client.query(super.to_s).first.expr0
47
46
  end
48
47
 
49
48
  def sum field
50
- super(mappings[field])
51
- sfdc_client.query(to_s).first.expr0
49
+ raise ArgumentError, 'field is required' if field.blank?
50
+ raise ArgumentError, "field '#{field}' does not exist on #{sobject}" unless mappings.key?(field.to_sym)
51
+
52
+ sfdc_client.query(super(mappings.fetch(field.to_sym)).to_s).first.expr0
52
53
  end
53
54
 
54
55
  def limit limit
55
- super
56
- limit == 1 ? to_a.first : self
56
+ limit == 1 ? super.to_a.first : super
57
57
  end
58
58
 
59
59
  def not args=nil, *rest
60
60
  return self if args.nil?
61
+
61
62
  super build_condition args, rest
62
- self
63
63
  end
64
64
 
65
65
  def where args=nil, *rest
66
66
  return self if args.nil?
67
- return clone_self_and_clear_cache.where(args, *rest) if @decorated_records.present?
68
67
  super build_condition args, rest
69
- self
70
68
  end
71
69
 
72
- def select *fields
73
- fields.map! { |field| mappings[field] }
74
- super *fields
70
+ def select *selected_fields
71
+ selected_fields.map! { |field| mappings[field] }
72
+ super *selected_fields
75
73
  end
76
74
 
77
75
  def find!(id)
@@ -100,8 +98,10 @@ module ActiveForce
100
98
  end
101
99
 
102
100
  def none
103
- @records = []
104
- where(id: '1'*18).where(id: '0'*18)
101
+ clone_and_set_instance_variables(
102
+ records: [],
103
+ conditions: [build_condition(id: '1' * 18), build_condition(id: '0' * 18)]
104
+ )
105
105
  end
106
106
 
107
107
  def loaded?
@@ -205,13 +205,6 @@ module ActiveForce
205
205
  sfdc_client.query(self.to_s)
206
206
  end
207
207
 
208
- def clone_self_and_clear_cache
209
- new_query = self.clone
210
- new_query.instance_variable_set(:@decorated_records, nil)
211
- new_query.instance_variable_set(:@records, nil)
212
- new_query
213
- end
214
-
215
208
  def build_order_by(args)
216
209
  args.map do |arg|
217
210
  case arg
@@ -228,6 +221,5 @@ module ActiveForce
228
221
  def order_type(type)
229
222
  type == :desc ? 'DESC' : 'ASC'
230
223
  end
231
-
232
224
  end
233
225
  end
@@ -31,33 +31,34 @@ module ActiveForce
31
31
  end
32
32
 
33
33
  def select *columns
34
- @query_fields = columns
35
- self
34
+ clone_and_set_instance_variables(query_fields: columns)
35
+ end
36
+
37
+ def where condition = nil
38
+ new_conditions = @conditions | [condition]
39
+ if new_conditions != @conditions
40
+ clone_and_set_instance_variables({conditions: new_conditions})
41
+ else
42
+ self
43
+ end
36
44
  end
37
45
 
38
46
  def not condition
39
- @conditions << "NOT ((#{ condition.join(') AND (') }))"
40
- self
47
+ condition ? where("NOT ((#{condition.join(') AND (')}))") : self
41
48
  end
42
49
 
43
50
  def or query
44
- @conditions = ["(#{ and_conditions }) OR (#{ query.and_conditions })"]
45
- self
46
- end
51
+ return self unless query
47
52
 
48
- def where condition = nil
49
- @conditions << condition if condition
50
- self
53
+ clone_and_set_instance_variables(conditions: ["(#{and_conditions}) OR (#{query.and_conditions})"])
51
54
  end
52
55
 
53
56
  def order order
54
- @order = order if order
55
- self
57
+ order ? clone_and_set_instance_variables(order: order) : self
56
58
  end
57
59
 
58
60
  def limit size
59
- @size = size if size
60
- self
61
+ size ? clone_and_set_instance_variables(size: size) : self
61
62
  end
62
63
 
63
64
  def limit_value
@@ -65,8 +66,7 @@ module ActiveForce
65
66
  end
66
67
 
67
68
  def offset offset
68
- @offset = offset
69
- self
69
+ clone_and_set_instance_variables(offset: offset)
70
70
  end
71
71
 
72
72
  def offset_value
@@ -74,8 +74,7 @@ module ActiveForce
74
74
  end
75
75
 
76
76
  def find id
77
- where "#{ @table_id } = '#{ id }'"
78
- limit 1
77
+ where("#{ @table_id } = '#{ id }'").limit 1
79
78
  end
80
79
 
81
80
  def first
@@ -87,18 +86,17 @@ module ActiveForce
87
86
  end
88
87
 
89
88
  def join object_query
90
- fields ["(#{ object_query.to_s })"]
91
- self
89
+ chained_query = self.clone
90
+ chained_query.fields ["(#{ object_query.to_s })"]
91
+ chained_query
92
92
  end
93
93
 
94
94
  def count
95
- @query_fields = ["count(Id)"]
96
- self
95
+ clone_and_set_instance_variables(query_fields: ["count(Id)"])
97
96
  end
98
97
 
99
98
  def sum field
100
- @query_fields = ["sum(#{field})"]
101
- self
99
+ clone_and_set_instance_variables(query_fields: ["sum(#{field})"])
102
100
  end
103
101
 
104
102
  protected
@@ -125,5 +123,13 @@ module ActiveForce
125
123
  def build_offset
126
124
  "OFFSET #{ @offset }" if @offset
127
125
  end
126
+
127
+ def clone_and_set_instance_variables instance_variable_hash={}
128
+ clone = self.clone
129
+ clone.instance_variable_set(:@decorated_records, nil)
130
+ clone.instance_variable_set(:@records, nil)
131
+ instance_variable_hash.each { |k,v| clone.instance_variable_set("@#{k.to_s}", v) }
132
+ clone
133
+ end
128
134
  end
129
135
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveForce
4
- VERSION = '0.16.0'
4
+ VERSION = '0.17.0'
5
5
  end
@@ -9,7 +9,7 @@ describe ActiveForce::ActiveQuery do
9
9
  })
10
10
  end
11
11
  let(:mappings){ { id: "Id", field: "Field__c", other_field: "Other_Field" } }
12
- let(:client){ double("client") }
12
+ let(:client) { double('client', query: nil) }
13
13
  let(:active_query){ described_class.new(sobject) }
14
14
  let(:api_result) do
15
15
  [
@@ -42,48 +42,45 @@ describe ActiveForce::ActiveQuery do
42
42
 
43
43
  describe "select only some field using mappings" do
44
44
  it "should return a query only with selected field" do
45
- active_query.select(:field)
46
- expect(active_query.to_s).to eq("SELECT Field__c FROM table_name")
45
+ new_query = active_query.select(:field)
46
+ expect(new_query.to_s).to eq("SELECT Field__c FROM table_name")
47
47
  end
48
48
  end
49
49
 
50
50
  describe "condition mapping" do
51
51
  it "maps conditions for a .where" do
52
- active_query.where(field: 123)
53
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
52
+ new_query = active_query.where(field: 123)
53
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
54
54
  end
55
55
 
56
56
  it 'transforms an array to a WHERE/IN clause' do
57
- active_query.where(field: ['foo', 'bar'])
58
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c IN ('foo','bar'))")
57
+ new_query = active_query.where(field: ['foo', 'bar'])
58
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c IN ('foo','bar'))")
59
59
  end
60
60
 
61
61
  it "encloses the value in quotes if it's a string" do
62
- active_query.where field: "hello"
63
- expect(active_query.to_s).to end_with("(Field__c = 'hello')")
62
+ new_query = active_query.where field: "hello"
63
+ expect(new_query.to_s).to end_with("(Field__c = 'hello')")
64
64
  end
65
65
 
66
66
  it "formats as YYYY-MM-DDThh:mm:ss-hh:mm and does not enclose in quotes if it's a DateTime" do
67
67
  value = DateTime.now
68
- active_query.where(field: value)
69
- expect(active_query.to_s).to end_with("(Field__c = #{value.iso8601})")
68
+ expect(active_query.where(field: value).to_s).to end_with("(Field__c = #{value.iso8601})")
70
69
  end
71
70
 
72
71
  it "formats as YYYY-MM-DDThh:mm:ss-hh:mm and does not enclose in quotes if it's a Time" do
73
72
  value = Time.now
74
- active_query.where(field: value)
75
- expect(active_query.to_s).to end_with("(Field__c = #{value.iso8601})")
73
+ expect(active_query.where(field: value).to_s).to end_with("(Field__c = #{value.iso8601})")
76
74
  end
77
75
 
78
76
  it "formats as YYYY-MM-DD and does not enclose in quotes if it's a Date" do
79
77
  value = Date.today
80
- active_query.where(field: value)
81
- expect(active_query.to_s).to end_with("(Field__c = #{value.iso8601})")
78
+ expect(active_query.where(field: value).to_s).to end_with("(Field__c = #{value.iso8601})")
82
79
  end
83
80
 
84
81
  it "puts NULL when a field is set as nil" do
85
- active_query.where field: nil
86
- expect(active_query.to_s).to end_with("(Field__c = NULL)")
82
+ new_query = active_query.where field: nil
83
+ expect(new_query.to_s).to end_with("(Field__c = NULL)")
87
84
  end
88
85
 
89
86
  describe 'bind parameters' do
@@ -95,36 +92,33 @@ describe ActiveForce::ActiveQuery do
95
92
  end
96
93
 
97
94
  it 'accepts bind parameters' do
98
- active_query.where('Field__c = ?', 123)
99
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
95
+ new_query = active_query.where('Field__c = ?', 123)
96
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
100
97
  end
101
98
 
102
99
  it 'accepts nil bind parameters' do
103
- active_query.where('Field__c = ?', nil)
104
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = NULL)")
100
+ new_query = active_query.where('Field__c = ?', nil)
101
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = NULL)")
105
102
  end
106
103
 
107
104
  it 'accepts multiple bind parameters' do
108
- active_query.where('Field__c = ? AND Other_Field__c = ? AND Name = ?', 123, 321, 'Bob')
109
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
105
+ new_query = active_query.where('Field__c = ? AND Other_Field__c = ? AND Name = ?', 123, 321, 'Bob')
106
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
110
107
  end
111
108
 
112
109
  it 'formats as YYYY-MM-DDThh:mm:ss-hh:mm and does not enclose in quotes if value is a DateTime' do
113
110
  value = DateTime.now
114
- active_query.where('Field__c > ?', value)
115
- expect(active_query.to_s).to end_with("(Field__c > #{value.iso8601})")
111
+ expect(active_query.where('Field__c > ?', value).to_s).to end_with("(Field__c > #{value.iso8601})")
116
112
  end
117
113
 
118
114
  it 'formats as YYYY-MM-DDThh:mm:ss-hh:mm and does not enclose in quotes if value is a Time' do
119
115
  value = Time.now
120
- active_query.where('Field__c > ?', value)
121
- expect(active_query.to_s).to end_with("(Field__c > #{value.iso8601})")
116
+ expect(active_query.where('Field__c > ?', value).to_s).to end_with("(Field__c > #{value.iso8601})")
122
117
  end
123
118
 
124
119
  it 'formats as YYYY-MM-DD and does not enclose in quotes if value is a Date' do
125
120
  value = Date.today
126
- active_query.where('Field__c > ?', value)
127
- expect(active_query.to_s).to end_with("(Field__c > #{value.iso8601})")
121
+ expect(active_query.where('Field__c > ?', value).to_s).to end_with("(Field__c > #{value.iso8601})")
128
122
  end
129
123
 
130
124
  it 'complains when there given an incorrect number of bind parameters' do
@@ -135,41 +129,41 @@ describe ActiveForce::ActiveQuery do
135
129
 
136
130
  context 'named bind parameters' do
137
131
  it 'accepts bind parameters' do
138
- active_query.where('Field__c = :field', field: 123)
139
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
132
+ new_query = active_query.where('Field__c = :field', field: 123)
133
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123)")
140
134
  end
141
135
 
142
136
  it 'accepts nil bind parameters' do
143
- active_query.where('Field__c = :field', field: nil)
144
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = NULL)")
137
+ new_query = active_query.where('Field__c = :field', field: nil)
138
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = NULL)")
145
139
  end
146
140
 
147
141
  it 'formats as YYYY-MM-DDThh:mm:ss-hh:mm and does not enclose in quotes if value is a DateTime' do
148
142
  value = DateTime.now
149
- active_query.where('Field__c < :field', field: value)
150
- expect(active_query.to_s).to end_with("(Field__c < #{value.iso8601})")
143
+ new_query = active_query.where('Field__c < :field', field: value)
144
+ expect(new_query.to_s).to end_with("(Field__c < #{value.iso8601})")
151
145
  end
152
146
 
153
147
  it 'formats as YYYY-MM-DDThh:mm:ss-hh:mm and does not enclose in quotes if value is a Time' do
154
148
  value = Time.now
155
- active_query.where('Field__c < :field', field: value)
156
- expect(active_query.to_s).to end_with("(Field__c < #{value.iso8601})")
149
+ new_query = active_query.where('Field__c < :field', field: value)
150
+ expect(new_query.to_s).to end_with("(Field__c < #{value.iso8601})")
157
151
  end
158
152
 
159
153
  it 'formats as YYYY-MM-DD and does not enclose in quotes if value is a Date' do
160
154
  value = Date.today
161
- active_query.where('Field__c < :field', field: value)
162
- expect(active_query.to_s).to end_with("(Field__c < #{value.iso8601})")
155
+ new_query = active_query.where('Field__c < :field', field: value)
156
+ expect(new_query.to_s).to end_with("(Field__c < #{value.iso8601})")
163
157
  end
164
158
 
165
159
  it 'accepts multiple bind parameters' do
166
- active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', field: 123, other_field: 321, name: 'Bob')
167
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
160
+ new_query = active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', field: 123, other_field: 321, name: 'Bob')
161
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
168
162
  end
169
163
 
170
164
  it 'accepts multiple bind parameters orderless' do
171
- active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', name: 'Bob', other_field: 321, field: 123)
172
- expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
165
+ new_query = active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', name: 'Bob', other_field: 321, field: 123)
166
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob')")
173
167
  end
174
168
 
175
169
  it 'complains when there given an incorrect number of bind parameters' do
@@ -198,29 +192,77 @@ describe ActiveForce::ActiveQuery do
198
192
  {"Id" => "0000000000EEEEEFFF"}
199
193
  ]
200
194
  end
195
+
201
196
  it 'allows method chaining' do
202
197
  result = active_query.where("Text_Label = 'foo'").where("Checkbox_Label = true")
203
198
  expect(result).to be_a described_class
204
199
  end
205
200
 
201
+ it 'does not execute a query' do
202
+ active_query.where('x')
203
+ expect(client).not_to have_received(:query)
204
+ end
205
+
206
+ context 'when calling `where` on an ActiveQuery object that already has records' do
207
+ context 'after the query result has been decorated' do
208
+ it 'returns a new ActiveQuery object' do
209
+ first_active_query = active_query.where("Text_Label = 'foo'")
210
+ first_active_query.to_a # decorates the results
211
+ second_active_query = first_active_query.where("Checkbox_Label = true")
212
+ second_active_query.to_a
213
+ expect(second_active_query).to be_a described_class
214
+ expect(second_active_query).not_to eq first_active_query
215
+ expect(second_active_query.to_s).not_to eq first_active_query.to_s
216
+ expect(second_active_query.to_a.size).to eq(1)
217
+ end
218
+ end
219
+ end
220
+
206
221
  context 'when calling `where` on an ActiveQuery object that already has records' do
207
- it 'returns a new ActiveQuery object' do
208
- first_active_query = active_query.where("Text_Label = 'foo'")
209
- first_active_query.inspect # so the query is executed
210
- second_active_query = first_active_query.where("Checkbox_Label = true")
211
- second_active_query.inspect
212
- expect(second_active_query).to be_a described_class
213
- expect(second_active_query).not_to eq first_active_query
222
+ context 'without the query result being decorated' do
223
+
224
+ it 'returns a new ActiveQuery object' do
225
+ first_active_query = active_query.where("Text_Label = 'foo'")
226
+ second_active_query = first_active_query.where("Checkbox_Label = true")
227
+ expect(second_active_query).to be_a described_class
228
+ expect(second_active_query).not_to eq first_active_query
229
+ expect(second_active_query.to_s).not_to eq first_active_query.to_s
230
+ expect(second_active_query.to_a.size).to eq(1)
231
+ end
214
232
  end
215
233
  end
234
+ end
216
235
 
236
+ describe '#not' do
237
+ it 'adds a not condition' do
238
+ expect(active_query.not(field: 'x').to_s).to end_with("WHERE (NOT ((Field__c = 'x')))")
239
+ end
240
+
241
+ it 'allows chaining' do
242
+ expect(active_query.where(field: 'x').not(field: 'y').where(field: 'z')).to be_a(described_class)
243
+ end
244
+
245
+ it 'does not mutate the original query' do
246
+ original = active_query.to_s
247
+ active_query.not(field: 'x')
248
+ expect(active_query.to_s).to eq(original)
249
+ end
250
+
251
+ it 'returns the original query if not given a condition' do
252
+ expect(active_query.not).to be(active_query)
253
+ end
254
+
255
+ it 'does not execute a query' do
256
+ active_query.not(field: 'x')
257
+ expect(client).not_to have_received(:query)
258
+ end
217
259
  end
218
260
 
219
261
  describe "#find_by" do
220
262
  it "should query the client, with the SFDC field names and correctly enclosed values" do
221
- expect(client).to receive :query
222
- active_query.find_by field: 123
223
- expect(active_query.to_s).to eq "SELECT Id FROM table_name WHERE (Field__c = 123) LIMIT 1"
263
+ expect(client).to receive(:query).with("SELECT Id FROM table_name WHERE (Field__c = 123) LIMIT 1")
264
+ new_query = active_query.find_by field: 123
265
+ expect(new_query).to be_nil
224
266
  end
225
267
  end
226
268
 
@@ -290,18 +332,18 @@ describe ActiveForce::ActiveQuery do
290
332
  let(:expected_query){ "SELECT Id FROM table_name WHERE (Backslash_Field__c = '\\\\' AND NumberField = 123 AND QuoteField = '\\' OR Id!=NULL OR Id=\\'')" }
291
333
 
292
334
  it 'escapes quotes and backslashes in bind parameters' do
293
- 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)
294
- expect(active_query.to_s).to eq(expected_query)
335
+ new_query = 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)
336
+ expect(new_query.to_s).to eq(expected_query)
295
337
  end
296
338
 
297
339
  it 'escapes quotes and backslashes in named bind parameters' do
298
- active_query.where('Backslash_Field__c = ? AND NumberField = ? AND QuoteField = ?', backslash_input, number_input, quote_input)
299
- expect(active_query.to_s).to eq(expected_query)
340
+ new_query = active_query.where('Backslash_Field__c = ? AND NumberField = ? AND QuoteField = ?', backslash_input, number_input, quote_input)
341
+ expect(new_query.to_s).to eq(expected_query)
300
342
  end
301
343
 
302
344
  it 'escapes quotes and backslashes in hash conditions' do
303
- active_query.where(backslash_field: backslash_input, number_field: number_input, quote_field: quote_input)
304
- 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=\\'')")
345
+ new_query = active_query.where(backslash_field: backslash_input, number_field: number_input, quote_field: quote_input)
346
+ expect(new_query.to_s).to eq("SELECT Id FROM table_name WHERE (Backslash_Field__c = '\\\\') AND (NumberField = 123) AND (QuoteField = '\\' OR Id!=NULL OR Id=\\'')")
305
347
  end
306
348
  end
307
349
 
@@ -40,6 +40,16 @@ describe ActiveForce::SObject do
40
40
  post.comments.to_a
41
41
  end
42
42
 
43
+ it 'is not mutated by #where' do
44
+ post.comments.where(body: 'test').to_a
45
+ expect(post.comments.to_s).to end_with("FROM Comment__c WHERE (PostId = '1')")
46
+ end
47
+
48
+ it 'is not mutated by #none' do
49
+ post.comments.none.to_a
50
+ expect(post.comments.to_s).to end_with("FROM Comment__c WHERE (PostId = '1')")
51
+ end
52
+
43
53
  describe 'to_s' do
44
54
  it "should return a SOQL statment" do
45
55
  soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PostId = '1')"
@@ -32,7 +32,7 @@ describe ActiveForce::Query do
32
32
  expect(query.all.to_s).to eq "SELECT Id, name, etc FROM table_name"
33
33
  end
34
34
 
35
- it "should ignore dupicated attributes in select statment" do
35
+ it "should ignore duplicated attributes in select statment" do
36
36
  query.fields ['Id', 'name', 'etc']
37
37
  expect(query.all.to_s).to eq "SELECT Id, name, etc FROM table_name"
38
38
  end
@@ -46,6 +46,19 @@ describe ActiveForce::Query do
46
46
  it "should add multiples conditions to a query with parentheses" do
47
47
  expect(query.where("condition1 = 1").where("condition2 = 2 OR condition3 = 3").to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (condition1 = 1) AND (condition2 = 2 OR condition3 = 3)"
48
48
  end
49
+
50
+ it "should not duplicate conditions" do
51
+ first_query = query.where("name = 'cool'").where("foo = 'baz'")
52
+ second_query = first_query.where("name = 'cool'")
53
+ expect(first_query.to_s).to eq(second_query.to_s)
54
+ expect(first_query.object_id).to eq(second_query.object_id)
55
+ end
56
+
57
+ it "should not update the original query" do
58
+ new_query = query.where("name = 'cool'")
59
+ expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
60
+ expect(new_query.to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (name = 'cool')"
61
+ end
49
62
  end
50
63
 
51
64
  describe ".not" do
@@ -68,12 +81,18 @@ describe ActiveForce::Query do
68
81
  it "should add a limit to a query" do
69
82
  expect(query.limit("25").to_s).to eq "SELECT Id, name, etc FROM table_name LIMIT 25"
70
83
  end
84
+
85
+ it "should not update the original query" do
86
+ new_query = query.limit("25")
87
+ expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
88
+ expect(new_query.to_s).to eq "SELECT Id, name, etc FROM table_name LIMIT 25"
89
+ end
71
90
  end
72
91
 
73
92
  describe ".limit_value" do
74
93
  it "should return the limit value" do
75
- query.limit(4)
76
- expect(query.limit_value).to eq 4
94
+ new_query = query.limit(4)
95
+ expect(new_query.limit_value).to eq 4
77
96
  end
78
97
  end
79
98
 
@@ -81,12 +100,18 @@ describe ActiveForce::Query do
81
100
  it "should add an offset to a query" do
82
101
  expect(query.offset(4).to_s).to eq "SELECT Id, name, etc FROM table_name OFFSET 4"
83
102
  end
103
+
104
+ it "should not update the original query" do
105
+ new_query = query.offset(4)
106
+ expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
107
+ expect(new_query.to_s).to eq "SELECT Id, name, etc FROM table_name OFFSET 4"
108
+ end
84
109
  end
85
110
 
86
111
  describe ".offset_value" do
87
112
  it "should return the offset value" do
88
- query.offset(4)
89
- expect(query.offset_value).to eq 4
113
+ new_query = query.offset(4)
114
+ expect(new_query.offset_value).to eq 4
90
115
  end
91
116
  end
92
117
 
@@ -104,6 +129,12 @@ describe ActiveForce::Query do
104
129
  it "should add a order condition in the statment with WHERE and LIMIT" do
105
130
  expect(query.where("condition1 = 1").order("name desc").limit(1).to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (condition1 = 1) ORDER BY name desc LIMIT 1"
106
131
  end
132
+
133
+ it "should not update the original query" do
134
+ ordered_query = query.order("name desc")
135
+ expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
136
+ expect(ordered_query.to_s).to eq "SELECT Id, name, etc FROM table_name ORDER BY name desc"
137
+ end
107
138
  end
108
139
 
109
140
  describe '.join' do
@@ -116,18 +147,36 @@ describe ActiveForce::Query do
116
147
  it 'should add another select statment on the current select' do
117
148
  expect(query.join(join_query).to_s).to eq 'SELECT Id, name, etc, (SELECT Id, name, etc FROM join_table_name) FROM table_name'
118
149
  end
150
+
151
+ it "should not update the original query" do
152
+ new_query = query.join(join_query)
153
+ expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
154
+ expect(new_query.to_s).to eq 'SELECT Id, name, etc, (SELECT Id, name, etc FROM join_table_name) FROM table_name'
155
+ end
119
156
  end
120
157
 
121
158
  describe '.first' do
122
159
  it 'should return the query for the first record' do
123
160
  expect(query.first.to_s).to eq 'SELECT Id, name, etc FROM table_name LIMIT 1'
124
161
  end
162
+
163
+ it "should not update the original query" do
164
+ new_query = query.first
165
+ expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
166
+ expect(new_query.to_s).to eq 'SELECT Id, name, etc FROM table_name LIMIT 1'
167
+ end
125
168
  end
126
169
 
127
170
  describe '.last' do
128
171
  it 'should return the query for the last record' do
129
172
  expect(query.last.to_s).to eq 'SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT 1'
130
173
  end
174
+
175
+ it "should not update the original query" do
176
+ new_query = query.last
177
+ expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
178
+ expect(new_query.to_s).to eq 'SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT 1'
179
+ end
131
180
  end
132
181
 
133
182
  describe ".count" do
@@ -138,6 +187,12 @@ describe ActiveForce::Query do
138
187
  it "should work with a condition" do
139
188
  expect(query.where("name = 'cool'").count.to_s).to eq "SELECT count(Id) FROM table_name WHERE (name = 'cool')"
140
189
  end
190
+
191
+ it "should not update the original query" do
192
+ query_with_count = query.where("name = 'cool'").count
193
+ expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
194
+ expect(query_with_count.to_s).to eq "SELECT count(Id) FROM table_name WHERE (name = 'cool')"
195
+ end
141
196
  end
142
197
 
143
198
  describe ".sum" do
@@ -315,18 +315,67 @@ describe ActiveForce::SObject do
315
315
  end
316
316
  end
317
317
 
318
- describe "#count" do
319
- let(:count_response){ [Restforce::Mash.new(expr0: 1)] }
318
+ describe '.count' do
319
+ let(:response) { [Restforce::Mash.new(expr0: 1)] }
320
320
 
321
- it "responds to count" do
322
- expect(Whizbang).to respond_to(:count)
321
+ before do
322
+ allow(client).to receive(:query).and_return(response)
323
+ end
324
+
325
+ it 'sends the correct query to the client' do
326
+ expected = 'SELECT count(Id) FROM Whizbang__c'
327
+ Whizbang.count
328
+ expect(client).to have_received(:query).with(expected)
323
329
  end
324
330
 
325
- it "sends the query to the client" do
326
- expect(client).to receive(:query).and_return(count_response)
331
+ it 'returns the result from the response' do
327
332
  expect(Whizbang.count).to eq(1)
328
333
  end
329
334
 
335
+ it 'works with .where' do
336
+ expected = 'SELECT count(Id) FROM Whizbang__c WHERE (Boolean_Label = true)'
337
+ Whizbang.where(boolean: true).count
338
+ expect(client).to have_received(:query).with(expected)
339
+ end
340
+ end
341
+
342
+ describe '.sum' do
343
+ let(:response) { [Restforce::Mash.new(expr0: 22)] }
344
+
345
+ before do
346
+ allow(client).to receive(:query).and_return(response)
347
+ end
348
+
349
+ it 'raises ArgumentError if given blank' do
350
+ expect { Whizbang.sum(nil) }.to raise_error(ArgumentError, 'field is required')
351
+ end
352
+
353
+ it 'raises ArgumentError if given invalid field' do
354
+ expect { Whizbang.sum(:invalid) }
355
+ .to raise_error(ArgumentError, /field 'invalid' does not exist on Whizbang/i)
356
+ end
357
+
358
+ it 'sends the correct query to the client' do
359
+ expected = 'SELECT sum(Percent_Label) FROM Whizbang__c'
360
+ Whizbang.sum(:percent)
361
+ expect(client).to have_received(:query).with(expected)
362
+ end
363
+
364
+ it 'works when given a string field' do
365
+ expected = 'SELECT sum(Percent_Label) FROM Whizbang__c'
366
+ Whizbang.sum('percent')
367
+ expect(client).to have_received(:query).with(expected)
368
+ end
369
+
370
+ it 'returns the result from the response' do
371
+ expect(Whizbang.sum(:percent)).to eq(22)
372
+ end
373
+
374
+ it 'works with .where' do
375
+ expected = 'SELECT sum(Percent_Label) FROM Whizbang__c WHERE (Boolean_Label = true)'
376
+ Whizbang.where(boolean: true).sum(:percent)
377
+ expect(client).to have_received(:query).with(expected)
378
+ end
330
379
  end
331
380
 
332
381
  describe "#find_by" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_force
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eloy Espinaco
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2023-07-31 00:00:00.000000000 Z
14
+ date: 2023-08-23 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activemodel