composite_primary_keys 8.1.7 → 8.1.8

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: cf0a1ef2ec3bc66204ea285b2a47f4ef85abb4b0968bad900d7a4cc4d8f4d154
4
- data.tar.gz: 94d5e203222b2ab17b9aba0f2a23a0e0df1bcc608083540cbb0765d887be0c61
3
+ metadata.gz: 98777241ea29aef880ba2b53631fb47686751a898df5b7200cd64a06fb84b838
4
+ data.tar.gz: 0f5d62c48beab81bcc7a31dd9a025af2867eeb8821482e2aabc0933ae10b95e5
5
5
  SHA512:
6
- metadata.gz: 4cf0795ee5311d91749c120764a8ab6ba3c748a8802c2176dfb927ea72de4f625d165d87c698e0128a49413e8b97b95a5ec832f91280d1e5d3acf4c328f70dbb
7
- data.tar.gz: 1f2789ece64261de5cd6fdc1b892495f4f2f8efc86a462e1f61924d9fa3417bbd479e6c3f7787285ea68e26869d72bfa1fff51d81f43465868a06af4aa87af89
6
+ metadata.gz: 498c215b18b6056347095a00e81edf399cb0a684c8130413fbb0db87181451bf0801d9c5446b2d34d707137df0e984267d79a9f432accf0287003719a1e229b3
7
+ data.tar.gz: 1ed759331f302c751f4f93899d3d95945c8c768cd279f817b6431add9c7d97e5caa6b47f8500b8871fa6993a145b288fc6e56f7067ab2f5c479956a10e303d71
data/History.rdoc CHANGED
@@ -1,3 +1,6 @@
1
+ == 8.1.8 (2020-01-01)
2
+ Fix handling CPK with fields containing comma, #505 (Sergey Semyonov)
3
+
1
4
  == 8.1.7 (2019-05-04)
2
5
 
3
6
  Fix key not being an array in subquery on has_many :through (Eric Proulx)
@@ -64,7 +64,7 @@ module ActiveRecord
64
64
  @preloaded_records.map { |record|
65
65
  key = Array(association_key_name).map do |key_name|
66
66
  record[key_name]
67
- end.join(CompositePrimaryKeys::ID_SEP)
67
+ end.to_composite_keys.to_s
68
68
 
69
69
  [record, key]
70
70
  }
@@ -77,7 +77,7 @@ module ActiveRecord
77
77
  # owner[owner_key_name].to_s
78
78
  Array(owner_key_name).map do |key_name|
79
79
  owner[key_name]
80
- end.join(CompositePrimaryKeys::ID_SEP)
80
+ end.to_composite_keys.to_s
81
81
  end
82
82
  else
83
83
  owners.group_by do |owner|
@@ -85,10 +85,9 @@ module ActiveRecord
85
85
  # owner[owner_key_name]
86
86
  Array(owner_key_name).map do |key_name|
87
87
  owner[key_name]
88
- end.join(CompositePrimaryKeys::ID_SEP)
88
+ end.to_composite_keys.to_s
89
89
  end
90
90
  end
91
-
92
91
  end
93
92
  end
94
93
  end
@@ -186,7 +186,7 @@ module ActiveRecord
186
186
  end
187
187
 
188
188
  def to_param
189
- persisted? ? to_key.join(CompositePrimaryKeys::ID_SEP) : nil
189
+ persisted? ? to_key.to_composite_keys.to_s : nil
190
190
  end
191
191
  end
192
192
  end
@@ -1,6 +1,7 @@
1
1
  module CompositePrimaryKeys
2
2
  ID_SEP = ','
3
3
  ID_SET_SEP = ';'
4
+ ESCAPE_CHAR = '^'
4
5
 
5
6
  module ArrayExtension
6
7
  def to_composite_keys
@@ -8,12 +9,27 @@ module CompositePrimaryKeys
8
9
  end
9
10
  end
10
11
 
11
- def self.normalize(ids)
12
+ # Convert mixed representation of CPKs (by strings or arrays) to normalized
13
+ # representation (just by arrays).
14
+ #
15
+ # `ids` is Array that may contain:
16
+ # 1. A CPK represented by an array or a string.
17
+ # 2. An array of CPKs represented by arrays or strings.
18
+ #
19
+ # There is an issue. Let `ids` contain an array with serveral strings. We can't distinguish case 1
20
+ # from case 2 there in general. E.g. the item can be an array containing appropriate number of strings,
21
+ # and each string can contain appropriate number of commas. We consider case 2 to win there.
22
+ def self.normalize(ids, cpk_size)
12
23
  ids.map do |id|
13
- if id.is_a?(Array)
14
- normalize(id)
15
- elsif id.is_a?(String) && id.index(ID_SEP)
16
- id.split(ID_SEP)
24
+ if Utils.cpk_as_array?(id, cpk_size) && id.any? { |item| !Utils.cpk_as_string?(item, cpk_size) }
25
+ # CPK as an array - case 1
26
+ id
27
+ elsif id.is_a?(Array)
28
+ # An array of CPKs - case 2
29
+ normalize(id, cpk_size)
30
+ elsif id.is_a?(String)
31
+ # CPK as a string - case 1
32
+ CompositeKeys.parse(id)
17
33
  else
18
34
  id
19
35
  end
@@ -27,7 +43,7 @@ module CompositePrimaryKeys
27
43
  when Array
28
44
  value.to_composite_keys
29
45
  when String
30
- self.new(value.split(ID_SEP))
46
+ value.split(ID_SEP).map { |key| Utils.unescape_string_key(key) }.to_composite_keys
31
47
  else
32
48
  raise(ArgumentError, "Unsupported type: #{value}")
33
49
  end
@@ -35,9 +51,36 @@ module CompositePrimaryKeys
35
51
 
36
52
  def to_s
37
53
  # Doing this makes it easier to parse Base#[](attr_name)
38
- join(ID_SEP)
54
+ map { |key| Utils.escape_string_key(key.to_s) }.join(ID_SEP)
55
+ end
56
+ end
57
+
58
+ module Utils
59
+ class << self
60
+ def escape_string_key(key)
61
+ key.gsub(Regexp.union(ESCAPE_CHAR, ID_SEP)) do |unsafe|
62
+ "#{ESCAPE_CHAR}#{unsafe.ord.to_s(16).upcase}"
63
+ end
64
+ end
65
+
66
+ def unescape_string_key(key)
67
+ key.gsub(/#{Regexp.escape(ESCAPE_CHAR)}[0-9a-fA-F]{2}/) do |escaped|
68
+ char = escaped.slice(1, 2).hex.chr
69
+ (char == ESCAPE_CHAR || char == ID_SEP) ? char : escaped
70
+ end
71
+ end
72
+
73
+ def cpk_as_array?(value, pk_size)
74
+ # We don't permit Array to be an element of CPK.
75
+ value.is_a?(Array) && value.size == pk_size && value.none? { |item| item.is_a?(Array) }
76
+ end
77
+
78
+ def cpk_as_string?(value, pk_size)
79
+ value.is_a?(String) && value.count(ID_SEP) == pk_size - 1
80
+ end
39
81
  end
40
82
  end
83
+ private_constant :Utils
41
84
  end
42
85
 
43
86
  Array.send(:include, CompositePrimaryKeys::ArrayExtension)
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  def quote_column_names(name)
5
5
  Array(name).map do |col|
6
6
  quote_column_name(col.to_s)
7
- end.join(CompositePrimaryKeys::ID_SEP)
7
+ end.to_composite_keys.to_s
8
8
  end
9
9
  end
10
10
  end
@@ -84,7 +84,7 @@ module CompositePrimaryKeys
84
84
 
85
85
  # CPK
86
86
  # expects_array = ids.first.kind_of?(Array)
87
- ids = CompositePrimaryKeys.normalize(ids)
87
+ ids = CompositePrimaryKeys.normalize(ids, @klass.primary_keys.length)
88
88
  expects_array = ids.flatten != ids.flatten(1)
89
89
 
90
90
  return ids.first if expects_array && ids.first.empty?
@@ -138,7 +138,7 @@ module CompositePrimaryKeys
138
138
 
139
139
  result = ids.map do |cpk_ids|
140
140
  cpk_ids = if cpk_ids.length == 1
141
- cpk_ids.first.split(CompositePrimaryKeys::ID_SEP).to_composite_keys
141
+ CompositePrimaryKeys::CompositeKeys.parse(cpk_ids.first)
142
142
  else
143
143
  cpk_ids.to_composite_keys
144
144
  end
@@ -2,7 +2,7 @@ module CompositePrimaryKeys
2
2
  module VERSION
3
3
  MAJOR = 8
4
4
  MINOR = 1
5
- TINY = 7
5
+ TINY = 8
6
6
  STRING = [MAJOR, MINOR, TINY].join('.')
7
7
  end
8
8
  end
@@ -202,8 +202,8 @@ create table seats (
202
202
  );
203
203
 
204
204
  create table capitols (
205
- country varchar(100) default null,
206
- city varchar(100) default null,
205
+ country varchar(100) not null,
206
+ city varchar(100) not null,
207
207
  primary key (country, city)
208
208
  );
209
209
 
@@ -21,4 +21,18 @@ class CompositeArraysTest < ActiveSupport::TestCase
21
21
  assert_equal CompositePrimaryKeys::CompositeKeys, keys.class
22
22
  assert_equal '1,2,3', keys.to_s
23
23
  end
24
+
25
+ def test_parse
26
+ assert_equal ['1', '2'], CompositePrimaryKeys::CompositeKeys.parse('1,2')
27
+ assert_equal ['The USA', '^Washington, D.C.'],
28
+ CompositePrimaryKeys::CompositeKeys.parse('The USA,^5EWashington^2C D.C.')
29
+ assert_equal ['The USA', '^Washington, D.C.'],
30
+ CompositePrimaryKeys::CompositeKeys.parse(['The USA', '^Washington, D.C.'])
31
+ end
32
+
33
+ def test_to_s
34
+ assert_equal '1,2', CompositePrimaryKeys::CompositeKeys.new([1, 2]).to_s
35
+ assert_equal 'The USA,^5EWashington^2C D.C.',
36
+ CompositePrimaryKeys::CompositeKeys.new(['The USA', '^Washington, D.C.']).to_s
37
+ end
24
38
  end
data/test/test_find.rb CHANGED
@@ -41,6 +41,14 @@ class TestFind < ActiveSupport::TestCase
41
41
  assert_equal(['The Netherlands', 'Amsterdam'], capitol.id)
42
42
  end
43
43
 
44
+ def test_find_with_strings_with_comma_as_composite_keys
45
+ capitol = Capitol.create!(country: 'The USA', city: 'Washington, D.C.')
46
+ assert_equal ['The USA', 'Washington, D.C.'], capitol.id
47
+
48
+ assert_equal capitol, Capitol.find(['The USA', 'Washington, D.C.'])
49
+ assert_equal capitol, Capitol.find(capitol.to_param)
50
+ end
51
+
44
52
  def test_find_each
45
53
  room_assignments = []
46
54
  RoomAssignment.find_each(:batch_size => 2) do |assignment|
data/test/test_ids.rb CHANGED
@@ -40,6 +40,9 @@ class TestIds < ActiveSupport::TestCase
40
40
  testing_with do
41
41
  assert_equal '1,1', @first.to_param if composite?
42
42
  end
43
+
44
+ capitol = Capitol.create!(country: 'The USA', city: 'Washington, D.C.')
45
+ assert_equal 'The USA,Washington^2C D.C.', capitol.to_param
43
46
  end
44
47
 
45
48
  def test_ids_to_s
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composite_primary_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.7
4
+ version: 8.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Savage
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-04 00:00:00.000000000 Z
11
+ date: 2020-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -28,30 +28,30 @@ dependencies:
28
28
  name: sqlite3
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 1.3.6
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 1.3.6
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pg
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.15'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.15'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: mysql2
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -285,7 +285,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
285
285
  - !ruby/object:Gem::Version
286
286
  version: '0'
287
287
  requirements: []
288
- rubygems_version: 3.0.3
288
+ rubygems_version: 3.0.6
289
289
  signing_key:
290
290
  specification_version: 4
291
291
  summary: Composite key support for ActiveRecord