torque-postgresql 2.0.6 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd314ab034c3d38a227829faf2d706b13625a5e4cc6393004f6dbe2890e784a8
4
- data.tar.gz: 035b0fc92e1ea2aedb507c18094e66025c0fe98838f8553f3abc64764a0b2f03
3
+ metadata.gz: '09e539b25b952fb4d48bd092a90bae032ad8c26f1584cc5a704cdf96985b1e7e'
4
+ data.tar.gz: d79aa047ac44dc47a25979f71a0b46dfcbdc573f28d18bfcccbcb59b0a693c7b
5
5
  SHA512:
6
- metadata.gz: 6eeaffde8209891fee13de7518acea135cfe5f35ecbc5aa59124faad208ed0545a24c2ffb993f10eb5e306c2ccb96036e1dbc812a11177ecdda5e3ead62e0dd7
7
- data.tar.gz: ad55cf8b8a5e9de71575c12e8452d440439529258752ceceb95da17111991b8a9ad90101806ab328d5993d328bec69a71feb4cc6b85431c9802d0c225557dbb5
6
+ metadata.gz: e2f75f66a5e0bed5f14006ff4ffd8e32f96bbe17f18ff4603c0706301c87114a1880b1a8f7bd530501d95c8f6dc10c563af6da5a2ba5f4414a31115c5510ca0f
7
+ data.tar.gz: 930b6381e8e920a274c411c2ba8abfe96a5b8462227c51dbf5ee9f2a41ac88b6f3a3946d4e49cab0017bb271c176d58c37690e5e646b8536476b83a1cba5c62b
@@ -123,7 +123,7 @@ module Torque
123
123
  SQL
124
124
 
125
125
  tables.map do |(table, refs)|
126
- [table, Coder.decode(refs)]
126
+ [table, PG::TextDecoder::Array.new.decode(refs)]
127
127
  end.to_h
128
128
  end
129
129
 
@@ -147,20 +147,6 @@ module Torque
147
147
  SQL
148
148
  end
149
149
 
150
- # Extracts the value from a PostgreSQL column default definition.
151
- def extract_value_from_default(default)
152
- case default
153
- # Array elements
154
- when /\AARRAY\[(.*)\]\z/
155
- # TODO: Improve this since it's not the most safe approach
156
- eval(default.gsub(/ARRAY|::\w+(\[\])?/, ''))
157
- else
158
- super
159
- end
160
- rescue SyntaxError
161
- # If somethin goes wrong with the eval, just return nil
162
- end
163
-
164
150
  end
165
151
  end
166
152
  end
@@ -43,13 +43,13 @@ module Torque
43
43
  inherited_tables = @connection.inherited_tables
44
44
  sorted_tables = @connection.tables.sort - @connection.views
45
45
 
46
- stream.puts " # These are the common tables managed"
46
+ stream.puts " # These are the common tables"
47
47
  (sorted_tables - inherited_tables.keys).each do |table_name|
48
48
  table(table_name, stream) unless ignored?(table_name)
49
49
  end
50
50
 
51
51
  if inherited_tables.present?
52
- stream.puts " # These are tables that has inheritance"
52
+ stream.puts " # These are tables that have inheritance"
53
53
  inherited_tables.each do |table_name, inherits|
54
54
  next if ignored?(table_name)
55
55
 
@@ -70,7 +70,11 @@ module Torque
70
70
 
71
71
  # Returns all values that an enum type can have.
72
72
  def enum_values(name)
73
- select_values("SELECT unnest(enum_range(NULL::#{name}))", 'SCHEMA')
73
+ select_values(<<-SQL.squish, 'SCHEMA')
74
+ SELECT enumlabel FROM pg_enum
75
+ WHERE enumtypid = #{quote(name)}::regtype::oid
76
+ ORDER BY enumsortorder
77
+ SQL
74
78
  end
75
79
 
76
80
  # Rewrite the method that creates tables to easily accept extra options
@@ -15,6 +15,8 @@ module Torque
15
15
  include DatabaseStatements
16
16
  include SchemaStatements
17
17
 
18
+ INJECT_WHERE_REGEX = /(DO UPDATE SET.*excluded\.[^ ]+) RETURNING/.freeze
19
+
18
20
  # Get the current PostgreSQL version as a Gem Version.
19
21
  def version
20
22
  @version ||= Gem::Version.new(
@@ -26,6 +28,20 @@ module Torque
26
28
  def extract_table_options!(options)
27
29
  super.merge(options.extract!(:inherits))
28
30
  end
31
+
32
+ # Allow filtered bulk insert by adding the where clause. This method is only used by
33
+ # +InsertAll+, so it somewhat safe to override it
34
+ def build_insert_sql(insert)
35
+ super.tap do |sql|
36
+ if insert.update_duplicates? && insert.where_condition?
37
+ if insert.returning
38
+ sql.gsub!(INJECT_WHERE_REGEX, "\\1 WHERE #{insert.where} RETURNING")
39
+ else
40
+ sql << " WHERE #{insert.where}"
41
+ end
42
+ end
43
+ end
44
+ end
29
45
  end
30
46
 
31
47
  ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend Adapter
@@ -16,15 +16,16 @@ module Torque
16
16
  elsif !target.empty?
17
17
  load_target.pluck(reflection.association_primary_key)
18
18
  else
19
- stale_state
19
+ stale_state || column_default_value
20
20
  end
21
21
  end
22
22
 
23
23
  def ids_writer(ids)
24
- owner.write_attribute(source_attr, ids.presence)
24
+ ids = ids.presence || column_default_value
25
+ owner.write_attribute(source_attr, ids)
25
26
  return unless owner.persisted? && owner.attribute_changed?(source_attr)
26
27
 
27
- owner.update_attribute(source_attr, ids.presence)
28
+ owner.update_attribute(source_attr, ids)
28
29
  end
29
30
 
30
31
  def size
@@ -59,9 +60,11 @@ module Torque
59
60
  target
60
61
  end
61
62
 
62
- def build_changes
63
+ def build_changes(from_target = false)
64
+ return yield if defined?(@_building_changes) && @_building_changes
65
+
63
66
  @_building_changes = true
64
- yield.tap { ids_writer(ids_reader) }
67
+ yield.tap { ids_writer(from_target ? ids_reader : stale_state) }
65
68
  ensure
66
69
  @_building_changes = nil
67
70
  end
@@ -110,7 +113,7 @@ module Torque
110
113
  end
111
114
 
112
115
  def insert_record(record, *)
113
- super.tap do |saved|
116
+ (record.persisted? || super).tap do |saved|
114
117
  ids_rewriter(record.read_attribute(klass_attr), :<<) if saved
115
118
  end
116
119
  end
@@ -179,23 +182,27 @@ module Torque
179
182
  def ids_rewriter(ids, operator)
180
183
  list = owner[source_attr] ||= []
181
184
  list = list.public_send(operator, ids)
182
- owner[source_attr] = list.uniq.compact.presence
185
+ owner[source_attr] = list.uniq.compact.presence || column_default_value
183
186
 
184
187
  return if @_building_changes || !owner.persisted?
185
188
  owner.update_attribute(source_attr, list)
186
189
  end
187
190
 
191
+ def column_default_value
192
+ owner.class.columns_hash[source_attr].default
193
+ end
194
+
188
195
  ## HAS MANY
189
196
  def replace_records(*)
190
- build_changes { super }
197
+ build_changes(true) { super }
191
198
  end
192
199
 
193
200
  def concat_records(*)
194
- build_changes { super }
201
+ build_changes(true) { super }
195
202
  end
196
203
 
197
204
  def delete_or_destroy(*)
198
- build_changes { super }
205
+ build_changes(true) { super }
199
206
  end
200
207
 
201
208
  def difference(a, b)
@@ -33,13 +33,13 @@ module Torque
33
33
  def values_methods
34
34
  return @values_methods if defined?(@values_methods)
35
35
 
36
- prefix = options.fetch(:prefix, nil).try(:<<, '_')
37
- suffix = options.fetch(:suffix, nil).try(:prepend, '_')
36
+ prefix = options.fetch(:prefix, nil)
37
+ suffix = options.fetch(:suffix, nil)
38
38
 
39
- prefix = attribute + '_' if prefix == true
40
- suffix = '_' + attribute if suffix == true
39
+ prefix = attribute if prefix == true
40
+ suffix = attribute if suffix == true
41
41
 
42
- base = "#{prefix}%s#{suffix}"
42
+ base = [prefix, '%s', suffix].compact.join('_')
43
43
 
44
44
  @values_methods = begin
45
45
  values.map do |val|
@@ -29,7 +29,7 @@ module Torque
29
29
  def include_on(klass, method_name = nil)
30
30
  method_name ||= Torque::PostgreSQL.config.enum.base_method
31
31
  Builder.include_on(klass, method_name, Builder::Enum) do |builder|
32
- defined_enums[builder.attribute.to_sym] = builder.subtype
32
+ defined_enums[builder.attribute.to_s] = builder.subtype.klass
33
33
  end
34
34
  end
35
35
 
@@ -32,7 +32,7 @@ module Torque
32
32
  def include_on(klass, method_name = nil)
33
33
  method_name ||= Torque::PostgreSQL.config.enum.set_method
34
34
  Builder.include_on(klass, method_name, Builder::Enum, set_features: true) do |builder|
35
- defined_enums[builder.attribute.to_sym] = builder.subtype
35
+ defined_enums[builder.attribute.to_s] = builder.subtype
36
36
  end
37
37
  end
38
38
 
@@ -40,7 +40,7 @@ module Torque
40
40
 
41
41
  pg_class = ::Arel::Table.new('pg_class')
42
42
  source = ::Arel::Table.new(subclass.table_name, as: 'source')
43
- quoted_id = ::Arel::Nodes::Quoted.new(self.class.connection.quote(id))
43
+ quoted_id = ::Arel::Nodes::Quoted.new(id)
44
44
 
45
45
  query = ::Arel::SelectManager.new(pg_class)
46
46
  query.join(source).on(pg_class['oid'].eq(source['tableoid']))
@@ -206,6 +206,12 @@ module Torque
206
206
  ::ActiveRecord::Reflection.add_reflection(self, name, reflection)
207
207
  end
208
208
 
209
+ # Allow extra keyword arguments to be sent to +InsertAll+
210
+ def upsert_all(attributes, **xargs)
211
+ xargs = xargs.merge(on_duplicate: :update)
212
+ ::ActiveRecord::InsertAll.new(self, attributes, **xargs).execute
213
+ end
214
+
209
215
  protected
210
216
 
211
217
  # Allow optional select attributes to be loaded manually when they are
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Torque
4
+ module PostgreSQL
5
+ module InsertAll
6
+ attr_reader :where
7
+
8
+ def initialize(*args, where: nil, **xargs)
9
+ super(*args, **xargs)
10
+
11
+ @where = where
12
+ end
13
+ end
14
+
15
+ module InsertAll::Builder
16
+ delegate :where, to: :insert_all
17
+
18
+ def where_condition?
19
+ !where.nil?
20
+ end
21
+ end
22
+
23
+ ActiveRecord::InsertAll.prepend InsertAll
24
+ ActiveRecord::InsertAll::Builder.include InsertAll::Builder
25
+ end
26
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Torque
4
4
  module PostgreSQL
5
- VERSION = '2.0.6'
5
+ VERSION = '2.1.3'
6
6
  end
7
7
  end
@@ -22,7 +22,7 @@ require 'torque/postgresql/autosave_association'
22
22
  require 'torque/postgresql/auxiliary_statement'
23
23
  require 'torque/postgresql/base'
24
24
  require 'torque/postgresql/inheritance'
25
- require 'torque/postgresql/coder'
25
+ require 'torque/postgresql/insert_all'
26
26
  require 'torque/postgresql/migration'
27
27
  require 'torque/postgresql/relation'
28
28
  require 'torque/postgresql/reflection'
@@ -0,0 +1,5 @@
1
+ FactoryBot.define do
2
+ factory :item do
3
+ name { Faker::Lorem.sentence }
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ class Item < ActiveRecord::Base
2
+ belongs_to_many :tags
3
+ end
@@ -0,0 +1,3 @@
1
+ class Question < ActiveRecord::Base
2
+ self.implicit_order_column = 'created_at'
3
+ end
@@ -0,0 +1,2 @@
1
+ class QuestionSelect < Question
2
+ end
data/spec/schema.rb CHANGED
@@ -11,13 +11,14 @@
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
13
  begin
14
- version = 63
14
+ version = 73
15
15
 
16
16
  raise SystemExit if ActiveRecord::Migrator.current_version == version
17
17
  ActiveRecord::Schema.define(version: version) do
18
18
  self.verbose = false
19
19
 
20
20
  # These are extensions that must be enabled in order to support this database
21
+ enable_extension "pgcrypto"
21
22
  enable_extension "plpgsql"
22
23
 
23
24
  # These are user-defined types used on this database
@@ -102,6 +103,13 @@ begin
102
103
  t.index ["author_id"], name: "index_posts_on_author_id", using: :btree
103
104
  end
104
105
 
106
+ create_table "items", force: :cascade do |t|
107
+ t.string "name"
108
+ t.bigint "tag_ids", array: true, default: "{1}"
109
+ t.datetime "created_at", null: false
110
+ t.datetime "updated_at", null: false
111
+ end
112
+
105
113
  create_table "users", force: :cascade do |t|
106
114
  t.string "name", null: false
107
115
  t.enum "role", subtype: :roles, default: :visitor
@@ -118,6 +126,12 @@ begin
118
126
  t.datetime "updated_at", null: false
119
127
  end
120
128
 
129
+ create_table "questions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
130
+ t.string "title"
131
+ t.datetime "created_at", null: false
132
+ t.datetime "updated_at", null: false
133
+ end
134
+
121
135
  create_table "activity_books", force: :cascade, inherits: :activities do |t|
122
136
  t.text "description"
123
137
  t.string "url"
@@ -132,6 +146,10 @@ begin
132
146
 
133
147
  create_table "activity_post_samples", force: :cascade, inherits: :activity_posts
134
148
 
149
+ create_table "question_selects", force: :cascade, inherits: :questions do |t|
150
+ t.string "options", array: true
151
+ end
152
+
135
153
  # create_table "activity_blanks", force: :cascade, inherits: :activities
136
154
 
137
155
  # create_table "activity_images", force: :cascade, inherits: [:activities, :images]
@@ -147,6 +147,22 @@ RSpec.describe 'BelongsToMany' do
147
147
  expect(record.tags.count).to be_eql(5)
148
148
  end
149
149
 
150
+ it 'does not trigger after commit on the associated record' do
151
+ called = false
152
+
153
+ tag = FactoryBot.create(:tag)
154
+ Tag.after_commit { called = true }
155
+
156
+ expect(called).to be_falsey
157
+
158
+ subject.tags << tag
159
+
160
+ expect(subject.tag_ids).to be_eql([tag.id])
161
+ expect(called).to be_falsey
162
+
163
+ Tag.reset_callbacks(:commit)
164
+ end
165
+
150
166
  it 'can build an associated record' do
151
167
  record = subject.tags.build(name: 'Test')
152
168
  expect(record).to be_a(other)
@@ -225,6 +241,17 @@ RSpec.describe 'BelongsToMany' do
225
241
  expect(subject.tags.size).to be_eql(0)
226
242
  end
227
243
 
244
+ it 'can clear the array' do
245
+ record = Video.create(title: 'B', tags: [initial])
246
+ expect(record.tags.size).to be_eql(1)
247
+
248
+ record.update(tag_ids: [])
249
+ record.reload
250
+
251
+ expect(record.tag_ids).to be_nil
252
+ expect(record.tags.size).to be_eql(0)
253
+ end
254
+
228
255
  it 'can have sum operations' do
229
256
  records = FactoryBot.create_list(:tag, 5)
230
257
  subject.tags.concat(records)
@@ -318,7 +345,30 @@ RSpec.describe 'BelongsToMany' do
318
345
  expect { query.load }.not_to raise_error
319
346
  end
320
347
 
321
- context "When record is not persisted" do
348
+ context 'When the attribute has a default value' do
349
+ subject { FactoryBot.create(:item) }
350
+
351
+ it 'will always return the column default value' do
352
+ expect(subject.tag_ids).to be_a(Array)
353
+ expect(subject.tag_ids).to be_eql([1])
354
+ end
355
+
356
+ it 'will keep the value as an array even when the association is cleared' do
357
+ records = FactoryBot.create_list(:tag, 5)
358
+ subject.tags.concat(records)
359
+
360
+ subject.reload
361
+ expect(subject.tag_ids).to be_a(Array)
362
+ expect(subject.tag_ids).not_to be_eql([1, *records.map(&:id)])
363
+
364
+ subject.tags.clear
365
+ subject.reload
366
+ expect(subject.tag_ids).to be_a(Array)
367
+ expect(subject.tag_ids).to be_eql([1])
368
+ end
369
+ end
370
+
371
+ context 'When record is not persisted' do
322
372
  let(:initial) { FactoryBot.create(:tag) }
323
373
 
324
374
  subject { Video.new(title: 'A', tags: [initial]) }
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'InsertAll' do
4
+ context 'on executing' do
5
+ before do
6
+ ActiveRecord::InsertAll.send(:public, :to_sql)
7
+ allow_any_instance_of(ActiveRecord::InsertAll).to receive(:execute, &:to_sql)
8
+ end
9
+
10
+ subject { Tag }
11
+
12
+ let(:entries) { [{ name: 'A' }, { name: 'B' }] }
13
+
14
+ it 'does not mess with insert_all' do
15
+ result = subject.insert_all(entries)
16
+ expect(result.squish).to be_eql(<<~SQL.squish)
17
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B')
18
+ ON CONFLICT DO NOTHING RETURNING "id"
19
+ SQL
20
+
21
+ result = subject.insert_all(entries, returning: %i[name])
22
+ expect(result.squish).to be_eql(<<~SQL.squish)
23
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B')
24
+ ON CONFLICT DO NOTHING RETURNING "name"
25
+ SQL
26
+
27
+ result = subject.insert_all(entries, returning: %i[id name])
28
+ expect(result.squish).to be_eql(<<~SQL.squish)
29
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B')
30
+ ON CONFLICT DO NOTHING RETURNING "id","name"
31
+ SQL
32
+ end
33
+
34
+ it 'does not mess with insert_all!' do
35
+ result = subject.insert_all!(entries)
36
+ expect(result.squish).to be_eql(<<~SQL.squish)
37
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B') RETURNING "id"
38
+ SQL
39
+
40
+ result = subject.insert_all!(entries, returning: %i[name])
41
+ expect(result.squish).to be_eql(<<~SQL.squish)
42
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B') RETURNING "name"
43
+ SQL
44
+ end
45
+
46
+ it 'does not mess with upsert without where' do
47
+ result = subject.upsert_all(entries)
48
+ expect(result.squish).to be_eql(<<~SQL.squish)
49
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B')
50
+ ON CONFLICT ("id") DO UPDATE SET "name"=excluded."name"
51
+ RETURNING "id"
52
+ SQL
53
+
54
+ result = subject.upsert_all(entries, returning: %i[name])
55
+ expect(result.squish).to be_eql(<<~SQL.squish)
56
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B')
57
+ ON CONFLICT ("id") DO UPDATE SET "name"=excluded."name"
58
+ RETURNING "name"
59
+ SQL
60
+ end
61
+
62
+ it 'does add the where condition without the returning clause' do
63
+ result = subject.upsert_all(entries, returning: false, where: '1=1')
64
+ expect(result.squish).to be_eql(<<~SQL.squish)
65
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B')
66
+ ON CONFLICT ("id") DO UPDATE SET "name"=excluded."name"
67
+ WHERE 1=1
68
+ SQL
69
+ end
70
+
71
+ it 'does add the where condition with the returning clause' do
72
+ result = subject.upsert_all(entries, where: '1=1')
73
+ expect(result.squish).to be_eql(<<~SQL.squish)
74
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B')
75
+ ON CONFLICT ("id") DO UPDATE SET "name"=excluded."name"
76
+ WHERE 1=1 RETURNING "id"
77
+ SQL
78
+ end
79
+
80
+ xit 'dows work with model-based where clause' do
81
+ result = subject.upsert_all(entries, where: Tag.where(name: 'C'))
82
+ expect(result.squish).to be_eql(<<~SQL.squish)
83
+ INSERT INTO "tags" ("name") VALUES ('A'), ('B')
84
+ ON CONFLICT ("id") DO UPDATE SET "name"=excluded."name"
85
+ WHERE "tags"."name" = 'C' RETURNING "id"
86
+ SQL
87
+ end
88
+ end
89
+ end
@@ -387,10 +387,10 @@ RSpec.describe 'TableInheritance' do
387
387
  base.create(title: 'Activity test')
388
388
  child.create(title: 'Activity book')
389
389
  other.create(name: 'An author name')
390
+ base.instance_variable_set(:@casted_dependents, nil)
390
391
  end
391
392
 
392
393
  it 'does not affect normal records' do
393
- base.instance_variable_set(:@casted_dependents, {})
394
394
  expect(base.first.cast_record).to be_a(base)
395
395
  expect(child.first.cast_record).to be_a(child)
396
396
  expect(other.first.cast_record).to be_a(other)
@@ -408,9 +408,28 @@ RSpec.describe 'TableInheritance' do
408
408
  end
409
409
 
410
410
  it 'does trigger record casting when accessed through inheritance' do
411
- base.instance_variable_set(:@casted_dependents, nil)
412
411
  expect(base.second.cast_record).to eql(child.first)
413
412
  end
413
+
414
+ context 'using uuid' do
415
+ let(:base) { Question }
416
+ let(:child) { QuestionSelect }
417
+
418
+ before :each do
419
+ base.create(title: 'Simple question')
420
+ child.create(title: 'Select question')
421
+ base.instance_variable_set(:@casted_dependents, nil)
422
+ end
423
+
424
+ it 'does not affect normal records' do
425
+ expect(base.first.cast_record).to be_a(base)
426
+ expect(child.first.cast_record).to be_a(child)
427
+ end
428
+
429
+ it 'does trigger record casting when accessed through inheritance' do
430
+ expect(base.second.cast_record).to eql(child.first)
431
+ end
432
+ end
414
433
  end
415
434
  end
416
435
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: torque-postgresql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.6
4
+ version: 2.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Silva
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-10 00:00:00.000000000 Z
11
+ date: 2021-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -215,12 +215,12 @@ files:
215
215
  - lib/torque/postgresql/auxiliary_statement.rb
216
216
  - lib/torque/postgresql/auxiliary_statement/settings.rb
217
217
  - lib/torque/postgresql/base.rb
218
- - lib/torque/postgresql/coder.rb
219
218
  - lib/torque/postgresql/collector.rb
220
219
  - lib/torque/postgresql/config.rb
221
220
  - lib/torque/postgresql/geometry_builder.rb
222
221
  - lib/torque/postgresql/i18n.rb
223
222
  - lib/torque/postgresql/inheritance.rb
223
+ - lib/torque/postgresql/insert_all.rb
224
224
  - lib/torque/postgresql/migration.rb
225
225
  - lib/torque/postgresql/migration/command_recorder.rb
226
226
  - lib/torque/postgresql/railtie.rb
@@ -242,6 +242,7 @@ files:
242
242
  - spec/en.yml
243
243
  - spec/factories/authors.rb
244
244
  - spec/factories/comments.rb
245
+ - spec/factories/item.rb
245
246
  - spec/factories/posts.rb
246
247
  - spec/factories/tags.rb
247
248
  - spec/factories/texts.rb
@@ -259,7 +260,10 @@ files:
259
260
  - spec/models/course.rb
260
261
  - spec/models/geometry.rb
261
262
  - spec/models/guest_comment.rb
263
+ - spec/models/item.rb
262
264
  - spec/models/post.rb
265
+ - spec/models/question.rb
266
+ - spec/models/question_select.rb
263
267
  - spec/models/tag.rb
264
268
  - spec/models/text.rb
265
269
  - spec/models/time_keeper.rb
@@ -270,13 +274,13 @@ files:
270
274
  - spec/tests/arel_spec.rb
271
275
  - spec/tests/auxiliary_statement_spec.rb
272
276
  - spec/tests/belongs_to_many_spec.rb
273
- - spec/tests/coder_spec.rb
274
277
  - spec/tests/collector_spec.rb
275
278
  - spec/tests/distinct_on_spec.rb
276
279
  - spec/tests/enum_set_spec.rb
277
280
  - spec/tests/enum_spec.rb
278
281
  - spec/tests/geometric_builder_spec.rb
279
282
  - spec/tests/has_many_spec.rb
283
+ - spec/tests/insert_all_spec.rb
280
284
  - spec/tests/interval_spec.rb
281
285
  - spec/tests/lazy_spec.rb
282
286
  - spec/tests/period_spec.rb
@@ -303,53 +307,57 @@ required_rubygems_version: !ruby/object:Gem::Requirement
303
307
  - !ruby/object:Gem::Version
304
308
  version: 1.8.11
305
309
  requirements: []
306
- rubygems_version: 3.1.4
310
+ rubygems_version: 3.1.2
307
311
  signing_key:
308
312
  specification_version: 4
309
313
  summary: ActiveRecord extension to access PostgreSQL advanced resources
310
314
  test_files:
311
- - spec/models/activity_post/sample.rb
315
+ - spec/en.yml
316
+ - spec/factories/authors.rb
317
+ - spec/factories/comments.rb
318
+ - spec/factories/item.rb
319
+ - spec/factories/posts.rb
320
+ - spec/factories/tags.rb
321
+ - spec/factories/texts.rb
322
+ - spec/factories/users.rb
323
+ - spec/factories/videos.rb
324
+ - spec/mocks/cache_query.rb
325
+ - spec/mocks/create_table.rb
312
326
  - spec/models/activity.rb
327
+ - spec/models/activity_book.rb
328
+ - spec/models/activity_post.rb
329
+ - spec/models/activity_post/sample.rb
313
330
  - spec/models/author.rb
314
331
  - spec/models/author_journalist.rb
315
332
  - spec/models/comment.rb
316
333
  - spec/models/course.rb
317
- - spec/models/post.rb
318
- - spec/models/text.rb
319
- - spec/models/user.rb
320
- - spec/models/activity_book.rb
321
- - spec/models/activity_post.rb
322
334
  - spec/models/geometry.rb
323
335
  - spec/models/guest_comment.rb
336
+ - spec/models/item.rb
337
+ - spec/models/post.rb
324
338
  - spec/models/tag.rb
339
+ - spec/models/text.rb
325
340
  - spec/models/time_keeper.rb
341
+ - spec/models/user.rb
326
342
  - spec/models/video.rb
327
- - spec/factories/authors.rb
328
- - spec/factories/comments.rb
329
- - spec/factories/posts.rb
330
- - spec/factories/tags.rb
331
- - spec/factories/texts.rb
332
- - spec/factories/users.rb
333
- - spec/factories/videos.rb
334
- - spec/tests/geometric_builder_spec.rb
335
- - spec/tests/range_spec.rb
343
+ - spec/models/question.rb
344
+ - spec/models/question_select.rb
345
+ - spec/spec_helper.rb
336
346
  - spec/tests/arel_spec.rb
337
- - spec/tests/enum_spec.rb
338
- - spec/tests/period_spec.rb
339
- - spec/tests/coder_spec.rb
347
+ - spec/tests/auxiliary_statement_spec.rb
348
+ - spec/tests/belongs_to_many_spec.rb
340
349
  - spec/tests/collector_spec.rb
341
350
  - spec/tests/distinct_on_spec.rb
351
+ - spec/tests/enum_set_spec.rb
352
+ - spec/tests/enum_spec.rb
353
+ - spec/tests/geometric_builder_spec.rb
354
+ - spec/tests/has_many_spec.rb
355
+ - spec/tests/insert_all_spec.rb
342
356
  - spec/tests/interval_spec.rb
343
357
  - spec/tests/lazy_spec.rb
358
+ - spec/tests/period_spec.rb
344
359
  - spec/tests/quoting_spec.rb
360
+ - spec/tests/range_spec.rb
345
361
  - spec/tests/relation_spec.rb
346
- - spec/tests/auxiliary_statement_spec.rb
347
- - spec/tests/enum_set_spec.rb
348
- - spec/tests/has_many_spec.rb
349
- - spec/tests/belongs_to_many_spec.rb
350
362
  - spec/tests/table_inheritance_spec.rb
351
- - spec/mocks/cache_query.rb
352
- - spec/mocks/create_table.rb
353
- - spec/en.yml
354
- - spec/spec_helper.rb
355
363
  - spec/schema.rb
@@ -1,133 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Torque
4
- module PostgreSQL
5
- module Coder
6
-
7
- # This class represents an Record to be encoded, instead of a literal Array
8
- Record = Class.new(Array)
9
-
10
- class << self
11
-
12
- NEED_QUOTE_FOR = /[\\"(){}, \t\n\r\v\f]/m
13
- DELIMITER = ','
14
-
15
- # This method replace the +read_array+ method from PG gem
16
- # See https://github.com/ged/ruby-pg/blob/master/ext/pg_text_decoder.c#L177
17
- # for more information
18
- def decode(value)
19
- # TODO: Use StringScanner
20
- # See http://ruby-doc.org/stdlib-1.9.3/libdoc/strscan/rdoc/StringScanner.html
21
- _decode(::StringIO.new(value))
22
- end
23
-
24
- # This method replace the ++ method from PG gem
25
- # See https://github.com/ged/ruby-pg/blob/master/ext/pg_text_encoder.c#L398
26
- # for more information
27
- def encode(value)
28
- _encode(value)
29
- end
30
-
31
- private
32
-
33
- def _decode(stream)
34
- quoted = 0
35
- escaped = false
36
- result = []
37
- part = String.new
38
-
39
- # Always start getting the non-collection character, the second char
40
- stream.getc if stream.pos == 0
41
-
42
- # Check for an empty list
43
- return result if %w[} )].include?(stream.getc)
44
-
45
- # If it's not an empty list, return one position before iterating
46
- stream.pos -= 1
47
- stream.each_char do |c|
48
-
49
- case
50
- when quoted < 1
51
- case
52
- when c == DELIMITER, c == '}', c == ')'
53
-
54
- unless escaped
55
- # Non-quoted empty string or NULL as extense
56
- part = nil if quoted == 0 && ( part.length == 0 || part == 'NULL' )
57
- result << part
58
- end
59
-
60
- return result unless c == DELIMITER
61
-
62
- escaped = false
63
- quoted = 0
64
- part = String.new
65
-
66
- when c == '"'
67
- quoted = 1
68
- when c == '{', c == '('
69
- result << _decode(stream)
70
- escaped = true
71
- else
72
- part << c
73
- end
74
- when escaped
75
- escaped = false
76
- part << c
77
- when c == '\\'
78
- escaped = true
79
- when c == '"'
80
- if stream.getc == '"'
81
- part << c
82
- else
83
- stream.pos -= 1
84
- quoted = -1
85
- end
86
- else
87
- if ( c == '"' || c == "'" ) && stream.getc != c
88
- stream.pos -= 1
89
- quoted = -1
90
- else
91
- part << c
92
- end
93
- end
94
-
95
- end
96
- end
97
-
98
- def _encode(list)
99
- is_record = list.is_a?(Record)
100
- list.map! do |part|
101
- case part
102
- when NilClass
103
- is_record ? '' : 'NULL'
104
- when Array
105
- _encode(part)
106
- else
107
- _quote(part.to_s)
108
- end
109
- end
110
-
111
- result = is_record ? '(%s)' : '{%s}'
112
- result % list.join(DELIMITER)
113
- end
114
-
115
- def _quote(string)
116
- len = string.length
117
-
118
- # Fast results
119
- return '""' if len == 0
120
- return '"NULL"' if len == 4 && string == 'NULL'
121
-
122
- # Check if the string don't need quotes
123
- return string unless string =~ NEED_QUOTE_FOR
124
-
125
- # Use the original string escape function
126
- PG::Connection.escape_string(string).inspect
127
- end
128
-
129
- end
130
-
131
- end
132
- end
133
- end
@@ -1,367 +0,0 @@
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