torque-postgresql 1.1.8 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/torque/postgresql.rb +0 -2
  3. data/lib/torque/postgresql/adapter.rb +0 -1
  4. data/lib/torque/postgresql/adapter/database_statements.rb +4 -15
  5. data/lib/torque/postgresql/adapter/schema_creation.rb +13 -23
  6. data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -21
  7. data/lib/torque/postgresql/adapter/schema_dumper.rb +71 -11
  8. data/lib/torque/postgresql/adapter/schema_statements.rb +2 -12
  9. data/lib/torque/postgresql/associations.rb +0 -3
  10. data/lib/torque/postgresql/associations/association.rb +0 -4
  11. data/lib/torque/postgresql/associations/association_scope.rb +18 -60
  12. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +12 -15
  13. data/lib/torque/postgresql/associations/preloader.rb +0 -32
  14. data/lib/torque/postgresql/associations/preloader/association.rb +13 -10
  15. data/lib/torque/postgresql/autosave_association.rb +4 -4
  16. data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
  17. data/lib/torque/postgresql/coder.rb +1 -2
  18. data/lib/torque/postgresql/config.rb +0 -6
  19. data/lib/torque/postgresql/inheritance.rb +13 -17
  20. data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
  21. data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -38
  22. data/lib/torque/postgresql/relation.rb +11 -16
  23. data/lib/torque/postgresql/relation/auxiliary_statement.rb +2 -8
  24. data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
  25. data/lib/torque/postgresql/version.rb +1 -1
  26. data/spec/en.yml +19 -0
  27. data/spec/factories/authors.rb +6 -0
  28. data/spec/factories/comments.rb +13 -0
  29. data/spec/factories/posts.rb +6 -0
  30. data/spec/factories/tags.rb +5 -0
  31. data/spec/factories/texts.rb +5 -0
  32. data/spec/factories/users.rb +6 -0
  33. data/spec/factories/videos.rb +5 -0
  34. data/spec/mocks/cache_query.rb +16 -0
  35. data/spec/mocks/create_table.rb +35 -0
  36. data/spec/models/activity.rb +3 -0
  37. data/spec/models/activity_book.rb +4 -0
  38. data/spec/models/activity_post.rb +7 -0
  39. data/spec/models/activity_post/sample.rb +4 -0
  40. data/spec/models/author.rb +4 -0
  41. data/spec/models/author_journalist.rb +4 -0
  42. data/spec/models/comment.rb +3 -0
  43. data/spec/models/course.rb +2 -0
  44. data/spec/models/geometry.rb +2 -0
  45. data/spec/models/guest_comment.rb +4 -0
  46. data/spec/models/post.rb +6 -0
  47. data/spec/models/tag.rb +2 -0
  48. data/spec/models/text.rb +2 -0
  49. data/spec/models/time_keeper.rb +2 -0
  50. data/spec/models/user.rb +8 -0
  51. data/spec/models/video.rb +2 -0
  52. data/spec/schema.rb +141 -0
  53. data/spec/spec_helper.rb +59 -0
  54. data/spec/tests/arel_spec.rb +72 -0
  55. data/spec/tests/auxiliary_statement_spec.rb +593 -0
  56. data/spec/tests/belongs_to_many_spec.rb +240 -0
  57. data/spec/tests/coder_spec.rb +367 -0
  58. data/spec/tests/collector_spec.rb +59 -0
  59. data/spec/tests/distinct_on_spec.rb +65 -0
  60. data/spec/tests/enum_set_spec.rb +306 -0
  61. data/spec/tests/enum_spec.rb +621 -0
  62. data/spec/tests/geometric_builder_spec.rb +221 -0
  63. data/spec/tests/has_many_spec.rb +390 -0
  64. data/spec/tests/interval_spec.rb +167 -0
  65. data/spec/tests/lazy_spec.rb +24 -0
  66. data/spec/tests/period_spec.rb +954 -0
  67. data/spec/tests/quoting_spec.rb +24 -0
  68. data/spec/tests/range_spec.rb +36 -0
  69. data/spec/tests/relation_spec.rb +57 -0
  70. data/spec/tests/table_inheritance_spec.rb +403 -0
  71. metadata +103 -15
  72. data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
  73. data/lib/torque/postgresql/schema_dumper.rb +0 -101
@@ -0,0 +1,240 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'BelongsToMany' do
4
+ context 'on model' do
5
+ let(:model) { Video }
6
+ let(:builder) { Torque::PostgreSQL::Associations::Builder::BelongsToMany }
7
+ let(:reflection) { Torque::PostgreSQL::Reflection::BelongsToManyReflection }
8
+ after { model._reflections = {} }
9
+
10
+ it 'has the builder method' do
11
+ expect(model).to respond_to(:belongs_to_many)
12
+ end
13
+
14
+ it 'triggers the correct builder and relation' do
15
+ expect(builder).to receive(:build).with(anything, :tests, nil, {}) do |_, name, _, _|
16
+ ActiveRecord::Reflection.create(:belongs_to_many, name, nil, {}, model)
17
+ end
18
+
19
+ expect(reflection).to receive(:new).with(:tests, nil, {}, model)
20
+
21
+ model.belongs_to_many(:tests)
22
+ end
23
+ end
24
+
25
+ context 'on association' do
26
+ let(:other) { Tag }
27
+ let(:initial) { FactoryBot.create(:tag) }
28
+
29
+ before { Video.belongs_to_many :tags }
30
+ subject { Video.create(title: 'A') }
31
+ after { Video._reflections = {} }
32
+
33
+ it 'has the method' do
34
+ expect(subject).to respond_to(:tags)
35
+ expect(subject._reflections).to include('tags')
36
+ end
37
+
38
+ it 'loads associated records' do
39
+ subject.update(tag_ids: [initial.id])
40
+ expect(subject.tags.to_sql).to be_eql(<<-SQL.squish)
41
+ SELECT "tags".* FROM "tags" WHERE "tags"."id" IN (#{initial.id})
42
+ SQL
43
+
44
+ expect(subject.tags.load).to be_a(ActiveRecord::Associations::CollectionProxy)
45
+ expect(subject.tags.to_a).to be_eql([initial])
46
+ end
47
+
48
+ it 'can be marked as loaded' do
49
+ expect(subject.tags.loaded?).to be_eql(false)
50
+ expect(subject.tags).to respond_to(:load_target)
51
+ expect(subject.tags.load_target).to be_eql([])
52
+ expect(subject.tags.loaded?).to be_eql(true)
53
+ end
54
+
55
+ it 'can find specific records' do
56
+ records = FactoryBot.create_list(:tag, 10)
57
+ subject.update(tag_ids: records.map(&:id))
58
+ ids = records.map(&:id).sample(5)
59
+
60
+ expect(subject.tags).to respond_to(:find)
61
+ records = subject.tags.find(*ids)
62
+
63
+ expect(records.size).to be_eql(5)
64
+ expect(records.map(&:id).sort).to be_eql(ids.sort)
65
+ end
66
+
67
+ it 'can return last n records' do
68
+ records = FactoryBot.create_list(:tag, 10)
69
+ subject.update(tag_ids: records.map(&:id))
70
+ ids = records.map(&:id).last(5)
71
+
72
+ expect(subject.tags).to respond_to(:last)
73
+ records = subject.tags.last(5)
74
+
75
+ expect(records.size).to be_eql(5)
76
+ expect(records.map(&:id).sort).to be_eql(ids.sort)
77
+ end
78
+
79
+ it 'can return first n records' do
80
+ records = FactoryBot.create_list(:tag, 10)
81
+ subject.update(tag_ids: records.map(&:id))
82
+ ids = records.map(&:id).first(5)
83
+
84
+ expect(subject.tags).to respond_to(:take)
85
+ records = subject.tags.take(5)
86
+
87
+ expect(records.size).to be_eql(5)
88
+ expect(records.map(&:id).sort).to be_eql(ids.sort)
89
+ end
90
+
91
+ it 'can build an associated record' do
92
+ record = subject.tags.build(name: 'Test')
93
+ expect(record).to be_a(other)
94
+ expect(record).not_to be_persisted
95
+ expect(record.name).to be_eql('Test')
96
+
97
+ expect(subject.save).to be_truthy
98
+ expect(subject.tag_ids).to be_eql([record.id])
99
+ expect(subject.tags.size).to be_eql(1)
100
+ end
101
+
102
+ it 'can create an associated record' do
103
+ record = subject.tags.create(name: 'Test')
104
+ expect(subject.tags).to respond_to(:create!)
105
+
106
+ expect(record).to be_a(other)
107
+ expect(record).to be_persisted
108
+ expect(record.name).to be_eql('Test')
109
+ expect(subject.tag_ids).to be_eql([record.id])
110
+ end
111
+
112
+ it 'can concat records' do
113
+ record = FactoryBot.create(:tag)
114
+ subject.update(tag_ids: [record.id])
115
+ expect(subject.tags.size).to be_eql(1)
116
+
117
+ subject.tags.concat(other.new(name: 'Test'))
118
+ subject.tags.reload
119
+ expect(subject.tags.size).to be_eql(2)
120
+ expect(subject.tag_ids.size).to be_eql(2)
121
+ expect(subject.tags.last.name).to be_eql('Test')
122
+ end
123
+
124
+ it 'can replace records' do
125
+ subject.tags << FactoryBot.create(:tag)
126
+ expect(subject.tags.size).to be_eql(1)
127
+
128
+ subject.tags.replace([other.new(name: 'Test 1'), other.new(name: 'Test 2')])
129
+ expect(subject.tags.size).to be_eql(2)
130
+ expect(subject.tags[0].name).to be_eql('Test 1')
131
+ expect(subject.tags[1].name).to be_eql('Test 2')
132
+ end
133
+
134
+ it 'can delete all records' do
135
+ subject.tags.concat(FactoryBot.create_list(:tag, 5))
136
+ expect(subject.tags.size).to be_eql(5)
137
+
138
+ subject.tags.delete_all
139
+ expect(subject.tags.size).to be_eql(0)
140
+ end
141
+
142
+ it 'can destroy all records' do
143
+ subject.tags.concat(FactoryBot.create_list(:tag, 5))
144
+ expect(subject.tags.size).to be_eql(5)
145
+
146
+ subject.tags.destroy_all
147
+ expect(subject.tags.size).to be_eql(0)
148
+ end
149
+
150
+ it 'can have sum operations' do
151
+ records = FactoryBot.create_list(:tag, 5)
152
+ subject.tags.concat(records)
153
+
154
+ result = records.map(&:id).reduce(:+)
155
+ expect(subject.tags).to respond_to(:sum)
156
+ expect(subject.tags.sum(:id)).to be_eql(result)
157
+ end
158
+
159
+ it 'can have a pluck operation' do
160
+ records = FactoryBot.create_list(:tag, 5)
161
+ subject.tags.concat(records)
162
+
163
+ result = records.map(&:name).sort
164
+ expect(subject.tags).to respond_to(:pluck)
165
+ expect(subject.tags.pluck(:name).sort).to be_eql(result)
166
+ end
167
+
168
+ it 'can be markes as empty' do
169
+ expect(subject.tags).to respond_to(:empty?)
170
+ expect(subject.tags.empty?).to be_truthy
171
+
172
+ subject.tags << FactoryBot.create(:tag)
173
+ expect(subject.tags.empty?).to be_falsey
174
+ end
175
+
176
+ it 'can check if a record is included on the list' do
177
+ outside = FactoryBot.create(:tag)
178
+ inside = FactoryBot.create(:tag)
179
+ subject.tags << inside
180
+
181
+ expect(subject.tags).to respond_to(:include?)
182
+ expect(subject.tags.include?(inside)).to be_truthy
183
+ expect(subject.tags.include?(outside)).to be_falsey
184
+ end
185
+
186
+ it 'can append records' do
187
+ subject.tags << other.new(name: 'Test 1')
188
+ expect(subject.tags.size).to be_eql(1)
189
+
190
+ subject.tags << other.new(name: 'Test 2')
191
+ expect(subject.tags.size).to be_eql(2)
192
+ expect(subject.tags.last.name).to be_eql('Test 2')
193
+ end
194
+
195
+ it 'can clear records' do
196
+ subject.tags << FactoryBot.create(:tag)
197
+ expect(subject.tags.size).to be_eql(1)
198
+
199
+ subject.tags.clear
200
+ expect(subject.tags.size).to be_eql(0)
201
+ end
202
+
203
+ it 'can reload records' do
204
+ expect(subject.tags.size).to be_eql(0)
205
+ subject.tags << FactoryBot.create(:tag)
206
+
207
+ subject.tags.reload
208
+ expect(subject.tags.size).to be_eql(1)
209
+ end
210
+
211
+ it 'can preload records' do
212
+ records = FactoryBot.create_list(:tag, 5)
213
+ subject.tags.concat(records)
214
+
215
+ entries = Video.all.includes(:tags).load
216
+
217
+ expect(entries.size).to be_eql(1)
218
+ expect(entries.first.tags).to be_loaded
219
+ expect(entries.first.tags.size).to be_eql(5)
220
+ end
221
+
222
+ it 'can joins records' do
223
+ query = Video.all.joins(:tags)
224
+ expect(query.to_sql).to match(/INNER JOIN "tags"/)
225
+ expect { query.load }.not_to raise_error
226
+ end
227
+
228
+ context "When record is not persisted" do
229
+ let(:initial) { FactoryBot.create(:tag) }
230
+ before { Video.belongs_to_many :tags }
231
+ subject { Video.new(title: 'A', tags: [initial]) }
232
+ after { Video._reflections = {} }
233
+
234
+ it 'loads associated records' do
235
+ expect(subject.tags.load).to be_a(ActiveRecord::Associations::CollectionProxy)
236
+ expect(subject.tags.to_a).to be_eql([initial])
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,367 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Complex coder', type: :helper do
4
+ let(:coder) { Torque::PostgreSQL::Coder }
5
+
6
+ context 'on decode' do
7
+
8
+ context 'one dimensional arrays' do
9
+ it 'returns an empty array' do
10
+ expect(coder.decode(%[{}])).to eql []
11
+ end
12
+
13
+ it 'returns an array of strings' do
14
+ expect(coder.decode(%[{1,2,3}])).to eql ['1','2','3']
15
+ end
16
+
17
+ it 'returns an array of strings, with nils replacing NULL characters' do
18
+ expect(coder.decode(%[{1,,NULL}])).to eql ['1',nil,nil]
19
+ end
20
+
21
+ it 'returns an array with the word NULL' do
22
+ expect(coder.decode(%[{1,"NULL",3}])).to eql ['1','NULL','3']
23
+ end
24
+
25
+ it 'returns an array of strings when containing commas in a quoted string' do
26
+ expect(coder.decode(%[{1,"2,3",4}])).to eql ['1','2,3','4']
27
+ end
28
+
29
+ it 'returns an array of strings when containing an escaped quote' do
30
+ expect(coder.decode(%[{1,"2\\",3",4}])).to eql ['1','2",3','4']
31
+ end
32
+
33
+ it 'returns an array of strings when containing an escaped backslash' do
34
+ expect(coder.decode(%[{1,"2\\\\",3,4}])).to eql ['1','2\\','3','4']
35
+ expect(coder.decode(%[{1,"2\\\\\\",3",4}])).to eql ['1','2\\",3','4']
36
+ end
37
+
38
+ it 'returns an array containing empty strings' do
39
+ expect(coder.decode(%[{1,"",3,""}])).to eql ['1', '', '3', '']
40
+ end
41
+
42
+ it 'returns an array containing unicode strings' do
43
+ expect(coder.decode(%[{"Paragraph 399(b)(i) – “valid leave” – meaning"}])).to eq(['Paragraph 399(b)(i) – “valid leave” – meaning'])
44
+ end
45
+ end
46
+
47
+ context 'two dimensional arrays' do
48
+ it 'returns an empty array' do
49
+ expect(coder.decode(%[{{}}])).to eql [[]]
50
+ expect(coder.decode(%[{{},{}}])).to eql [[],[]]
51
+ end
52
+
53
+ it 'returns an array of strings with a sub array' do
54
+ expect(coder.decode(%[{1,{2,3},4}])).to eql ['1',['2','3'],'4']
55
+ end
56
+
57
+ it 'returns an array of strings with a sub array' do
58
+ expect(coder.decode(%[{1,{"2,3"},4}])).to eql ['1',['2,3'],'4']
59
+ end
60
+
61
+ it 'returns an array of strings with a sub array and a quoted }' do
62
+ expect(coder.decode(%[{1,{"2,}3",,NULL},4}])).to eql ['1',['2,}3',nil,nil],'4']
63
+ end
64
+
65
+ it 'returns an array of strings with a sub array and a quoted {' do
66
+ expect(coder.decode(%[{1,{"2,{3"},4}])).to eql ['1',['2,{3'],'4']
67
+ end
68
+
69
+ it 'returns an array of strings with a sub array and a quoted { and escaped quote' do
70
+ expect(coder.decode(%[{1,{"2\\",{3"},4}])).to eql ['1',['2",{3'],'4']
71
+ end
72
+
73
+ it 'returns an array of strings with a sub array with empty strings' do
74
+ expect(coder.decode(%[{1,{""},4,{""}}])).to eql ['1',[''],'4',['']]
75
+ end
76
+ end
77
+
78
+ context 'three dimensional arrays' do
79
+ it 'returns an empty array' do
80
+ expect(coder.decode(%[{{{}}}])).to eql [[[]]]
81
+ expect(coder.decode(%[{{{},{}},{{},{}}}])).to eql [[[],[]],[[],[]]]
82
+ end
83
+
84
+ it 'returns an array of strings with sub arrays' do
85
+ expect(coder.decode(%[{1,{2,{3,4}},{NULL,,6},7}])).to eql ['1',['2',['3','4']],[nil,nil,'6'],'7']
86
+ end
87
+ end
88
+
89
+ context 'record syntax' do
90
+ it 'returns an empty array' do
91
+ expect(coder.decode(%[()])).to eql []
92
+ end
93
+
94
+ it 'returns an array of strings' do
95
+ expect(coder.decode(%[(1,2,3)])).to eql ['1','2','3']
96
+ end
97
+
98
+ it 'returns an array of strings, with nils replacing NULL characters' do
99
+ expect(coder.decode(%[(1,,NULL)])).to eql ['1',nil,nil]
100
+ end
101
+
102
+ it 'returns an array with the word NULL' do
103
+ expect(coder.decode(%[(1,"NULL",3)])).to eql ['1','NULL','3']
104
+ end
105
+
106
+ it 'returns an array of strings when containing commas in a quoted string' do
107
+ expect(coder.decode(%[(1,"2,3",4)])).to eql ['1','2,3','4']
108
+ end
109
+
110
+ it 'returns an array of strings when containing an escaped quote' do
111
+ expect(coder.decode(%[(1,"2\\",3",4)])).to eql ['1','2",3','4']
112
+ end
113
+
114
+ it 'returns an array of strings when containing an escaped backslash' do
115
+ expect(coder.decode(%[(1,"2\\\\",3,4)])).to eql ['1','2\\','3','4']
116
+ expect(coder.decode(%[(1,"2\\\\\\",3",4)])).to eql ['1','2\\",3','4']
117
+ end
118
+
119
+ it 'returns an array containing empty strings' do
120
+ expect(coder.decode(%[(1,"",3,"")])).to eql ['1', '', '3', '']
121
+ end
122
+
123
+ it 'returns an array containing unicode strings' do
124
+ expect(coder.decode(%[("Paragraph 399(b)(i) – “valid leave” – meaning")])).to eq(['Paragraph 399(b)(i) – “valid leave” – meaning'])
125
+ end
126
+ end
127
+
128
+ context 'array of records' do
129
+ it 'returns an empty array' do
130
+ expect(coder.decode(%[{()}])).to eql [[]]
131
+ expect(coder.decode(%[{(),()}])).to eql [[],[]]
132
+ end
133
+
134
+ it 'returns an array of strings with a sub array' do
135
+ expect(coder.decode(%[{1,(2,3),4}])).to eql ['1',['2','3'],'4']
136
+ end
137
+
138
+ it 'returns an array of strings with a sub array' do
139
+ expect(coder.decode(%[{1,("2,3"),4}])).to eql ['1',['2,3'],'4']
140
+ end
141
+
142
+ it 'returns an array of strings with a sub array and a quoted }' do
143
+ expect(coder.decode(%[{1,("2,}3",,NULL),4}])).to eql ['1',['2,}3',nil,nil],'4']
144
+ end
145
+
146
+ it 'returns an array of strings with a sub array and a quoted {' do
147
+ expect(coder.decode(%[{1,("2,{3"),4}])).to eql ['1',['2,{3'],'4']
148
+ end
149
+
150
+ it 'returns an array of strings with a sub array and a quoted { and escaped quote' do
151
+ expect(coder.decode(%[{1,("2\\",{3"),4}])).to eql ['1',['2",{3'],'4']
152
+ end
153
+
154
+ it 'returns an array of strings with a sub array with empty strings' do
155
+ expect(coder.decode(%[{1,(""),4,("")}])).to eql ['1',[''],'4',['']]
156
+ end
157
+ end
158
+
159
+ context 'mix of record and array' do
160
+ it 'returns an empty array' do
161
+ expect(coder.decode(%[({()})])).to eql [[[]]]
162
+ expect(coder.decode(%[{({},{}),{(),{}}}])).to eql [[[],[]],[[],[]]]
163
+ end
164
+
165
+ it 'returns an array of strings with sub arrays' do
166
+ expect(coder.decode(%[{1,(2,{3,4}),(NULL,,6),7}])).to eql ['1',['2',['3','4']],[nil,nil,'6'],'7']
167
+ end
168
+ end
169
+
170
+ context 'record complex sample' do
171
+ it 'may have double double quotes translate to single double quotes' do
172
+ expect(coder.decode(%[("Test with double "" quoutes")])).to eql ['Test with double " quoutes']
173
+ end
174
+
175
+ it 'double double quotes may occur any number of times' do
176
+ expect(coder.decode(%[("Only one ""","Now "" two "".",""",""{""}","""""")])).to eql ['Only one "', 'Now " two ".', '","{"}', '""']
177
+ end
178
+
179
+ it 'may have any kind of value' do
180
+ expect(coder.decode(%[(String,123456,false,true,"2016-01-01 12:00:00",{1,2,3})])).to eql ['String', '123456', 'false', 'true', '2016-01-01 12:00:00', ['1', '2', '3']]
181
+ end
182
+ end
183
+
184
+ end
185
+
186
+ context 'on encode' do
187
+ let(:record) { Torque::PostgreSQL::Coder::Record }
188
+
189
+ context 'one dimensional arrays' do
190
+ it 'receives an empty array' do
191
+ expect(coder.encode([])).to eql %[{}]
192
+ end
193
+
194
+ it 'receives an array of strings' do
195
+ expect(coder.encode(['1','2','3'])).to eql %[{1,2,3}]
196
+ end
197
+
198
+ it 'receives an array of strings, with nils replacing NULL characters' do
199
+ expect(coder.encode(['1',nil,nil])).to eql %[{1,NULL,NULL}]
200
+ end
201
+
202
+ it 'receives an array with the word NULL' do
203
+ expect(coder.encode(['1','NULL','3'])).to eql %[{1,"NULL",3}]
204
+ end
205
+
206
+ it 'receives an array of strings when containing commas in a quoted string' do
207
+ expect(coder.encode(['1','2,3','4'])).to eql %[{1,"2,3",4}]
208
+ end
209
+
210
+ it 'receives an array of strings when containing an escaped quote' do
211
+ expect(coder.encode(['1','2",3','4'])).to eql %[{1,"2\\",3",4}]
212
+ end
213
+
214
+ it 'receives an array of strings when containing an escaped backslash' do
215
+ expect(coder.encode(['1','2\\','3','4'])).to eql %[{1,"2\\\\",3,4}]
216
+ expect(coder.encode(['1','2\\",3','4'])).to eql %[{1,"2\\\\\\",3",4}]
217
+ end
218
+
219
+ it 'receives an array containing empty strings' do
220
+ expect(coder.encode(['1', '', '3', ''])).to eql %[{1,"",3,""}]
221
+ end
222
+
223
+ it 'receives an array containing unicode strings' do
224
+ expect(coder.encode(['Paragraph 399(b)(i) – “valid leave” – meaning'])).to eql %[{"Paragraph 399(b)(i) – “valid leave” – meaning"}]
225
+ end
226
+ end
227
+
228
+ context 'two dimensional arrays' do
229
+ it 'receives an empty array' do
230
+ expect(coder.encode([[]])).to eql %[{{}}]
231
+ expect(coder.encode([[],[]])).to eql %[{{},{}}]
232
+ end
233
+
234
+ it 'receives an array of strings with a sub array' do
235
+ expect(coder.encode(['1',['2','3'],'4'])).to eql %[{1,{2,3},4}]
236
+ end
237
+
238
+ it 'receives an array of strings with a sub array' do
239
+ expect(coder.encode(['1',['2,3'],'4'])).to eql %[{1,{"2,3"},4}]
240
+ end
241
+
242
+ it 'receives an array of strings with a sub array and a quoted }' do
243
+ expect(coder.encode(['1',['2,}3',nil,nil],'4'])).to eql %[{1,{"2,}3",NULL,NULL},4}]
244
+ end
245
+
246
+ it 'receives an array of strings with a sub array and a quoted {' do
247
+ expect(coder.encode(['1',['2,{3'],'4'])).to eql %[{1,{"2,{3"},4}]
248
+ end
249
+
250
+ it 'receives an array of strings with a sub array and a quoted { and escaped quote' do
251
+ expect(coder.encode(['1',['2",{3'],'4'])).to eql %[{1,{"2\\",{3"},4}]
252
+ end
253
+
254
+ it 'receives an array of strings with a sub array with empty strings' do
255
+ expect(coder.encode(['1',[''],'4',['']])).to eql %[{1,{""},4,{""}}]
256
+ end
257
+ end
258
+
259
+ context 'three dimensional arrays' do
260
+ it 'receives an empty array' do
261
+ expect(coder.encode([[[]]])).to eql %[{{{}}}]
262
+ expect(coder.encode([[[],[]],[[],[]]])).to eql %[{{{},{}},{{},{}}}]
263
+ end
264
+
265
+ it 'receives an array of strings with sub arrays' do
266
+ expect(coder.encode(['1',['2',['3','4']],[nil,nil,'6'],'7'])).to eql %[{1,{2,{3,4}},{NULL,NULL,6},7}]
267
+ end
268
+ end
269
+
270
+ context 'record syntax' do
271
+ it 'receives an empty array' do
272
+ expect(coder.encode( record.new )).to eql %[()]
273
+ end
274
+
275
+ it 'receives an array of strings' do
276
+ expect(coder.encode( record.new(['1','2','3']) )).to eql %[(1,2,3)]
277
+ end
278
+
279
+ it 'receives an array of strings, with nils replacing NULL characters' do
280
+ expect(coder.encode( record.new(['1',nil,nil]) )).to eql %[(1,,)]
281
+ end
282
+
283
+ it 'receives an array with the word NULL' do
284
+ expect(coder.encode( record.new(['1','NULL','3']) )).to eql %[(1,"NULL",3)]
285
+ end
286
+
287
+ it 'receives an array of strings when containing commas in a quoted string' do
288
+ expect(coder.encode( record.new(['1','2,3','4']) )).to eql %[(1,"2,3",4)]
289
+ end
290
+
291
+ it 'receives an array of strings when containing an escaped quote' do
292
+ expect(coder.encode( record.new(['1','2",3','4']) )).to eql %[(1,"2\\",3",4)]
293
+ end
294
+
295
+ it 'receives an array of strings when containing an escaped backslash' do
296
+ expect(coder.encode( record.new(['1','2\\','3','4']) )).to eql %[(1,"2\\\\",3,4)]
297
+ expect(coder.encode( record.new(['1','2\\",3','4']) )).to eql %[(1,"2\\\\\\",3",4)]
298
+ end
299
+
300
+ it 'receives an array containing empty strings' do
301
+ expect(coder.encode( record.new(['1', '', '3', '']) )).to eql %[(1,"",3,"")]
302
+ end
303
+
304
+ it 'receives an array containing unicode strings' do
305
+ expect(coder.encode( record.new(['Paragraph 399(b)(i) – “valid leave” – meaning']) )).to eql %[("Paragraph 399(b)(i) – “valid leave” – meaning")]
306
+ end
307
+ end
308
+
309
+ context 'array of records' do
310
+ it 'receives an empty array' do
311
+ expect(coder.encode([record.new])).to eql %[{()}]
312
+ expect(coder.encode([record.new,record.new])).to eql %[{(),()}]
313
+ end
314
+
315
+ it 'receives an array of strings with a sub array' do
316
+ expect(coder.encode(['1',record.new(['2','3']),'4'])).to eql %[{1,(2,3),4}]
317
+ end
318
+
319
+ it 'receives an array of strings with a sub array' do
320
+ expect(coder.encode(['1',record.new(['2,3']),'4'])).to eql %[{1,("2,3"),4}]
321
+ end
322
+
323
+ it 'receives an array of strings with a sub array and a quoted }' do
324
+ expect(coder.encode(['1',record.new(['2,}3',nil,nil]),'4'])).to eql %[{1,("2,}3",,),4}]
325
+ end
326
+
327
+ it 'receives an array of strings with a sub array and a quoted {' do
328
+ expect(coder.encode(['1',record.new(['2,{3']),'4'])).to eql %[{1,("2,{3"),4}]
329
+ end
330
+
331
+ it 'receives an array of strings with a sub array and a quoted { and escaped quote' do
332
+ expect(coder.encode(['1',record.new(['2",{3']),'4'])).to eql %[{1,("2\\",{3"),4}]
333
+ end
334
+
335
+ it 'receives an array of strings with a sub array with empty strings' do
336
+ expect(coder.encode(['1',record.new(['']),'4',record.new([''])])).to eql %[{1,(""),4,("")}]
337
+ end
338
+ end
339
+
340
+ context 'mix of record and array' do
341
+ it 'receives an empty array' do
342
+ expect(coder.encode( record.new([[record.new,nil]]) )).to eql %[({(),NULL})]
343
+ expect(coder.encode( [record.new([[], []]),[record.new,[]]] )).to eql %[{({},{}),{(),{}}}]
344
+ end
345
+
346
+ it 'receives an array of strings with sub arrays' do
347
+ expect(coder.encode(['1',record.new(['2',['3','4']]),record.new([nil,nil,'6']),'7'])).to eql %[{1,(2,{3,4}),(,,6),7}]
348
+ end
349
+ end
350
+
351
+ context 'record complex sample' do
352
+ it 'may have double double quotes translate to single double quotes' do
353
+ expect(coder.encode( record.new(['Test with double " quoutes']) )).to eql %[("Test with double \\" quoutes")]
354
+ end
355
+
356
+ it 'double double quotes may occur any number of times' do
357
+ expect(coder.encode( record.new(['Only one "', 'Now " two ".', '","{"}', '""']) )).to eql %[("Only one \\"","Now \\" two \\".","\\",\\"{\\"}","\\"\\"")]
358
+ end
359
+
360
+ it 'may have any kind of value' do
361
+ expect(coder.encode( record.new(['String', '123456', 'false', 'true', '2016-01-01 12:00:00', ['1', '2', '3']]) )).to eql %[(String,123456,false,true,"2016-01-01 12:00:00",{1,2,3})]
362
+ end
363
+ end
364
+
365
+ end
366
+
367
+ end