masking 1.1.0 → 1.1.2.pre.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +9 -4
  3. data/.github/workflows/acceptance_test_mariadb.yml +57 -1
  4. data/.github/workflows/acceptance_test_mysql.yml +1 -1
  5. data/.rubocop.yml +7 -1
  6. data/.rubocop_todo.yml +105 -0
  7. data/.ruby-version +1 -1
  8. data/CHANGELOG.md +19 -0
  9. data/CODEOWNERS +1 -0
  10. data/Dockerfile +5 -3
  11. data/Dockerfile.ghcr +10 -0
  12. data/Gemfile +20 -0
  13. data/Gemfile.lock +67 -51
  14. data/README.md +38 -26
  15. data/acceptance/expected_error_result.txt +2 -0
  16. data/acceptance/expected_query_result.txt +36 -30
  17. data/acceptance/import_dumpfile.sql +3 -1
  18. data/acceptance/masking.yml +2 -0
  19. data/acceptance/run_test.sh +18 -6
  20. data/docker-compose/mariadb1010.yml +17 -0
  21. data/docker-compose/mariadb1011.yml +17 -0
  22. data/docker-compose/mariadb105.yml +17 -0
  23. data/docker-compose/mariadb106.yml +17 -0
  24. data/docker-compose/mariadb107.yml +17 -0
  25. data/docker-compose/mariadb108.yml +17 -0
  26. data/docker-compose/mariadb109.yml +17 -0
  27. data/lib/masking/cli/error_message.rb +16 -9
  28. data/lib/masking/config/target_columns/column.rb +19 -5
  29. data/lib/masking/config/target_columns/method/string_binary_distinctor.rb +3 -3
  30. data/lib/masking/config/target_columns/method/type/base.rb +25 -0
  31. data/lib/masking/config/target_columns/method/type/binary.rb +19 -0
  32. data/lib/masking/config/target_columns/method/type/boolean.rb +27 -0
  33. data/lib/masking/config/target_columns/method/type/date.rb +28 -0
  34. data/lib/masking/config/target_columns/method/type/extension/ignore_null.rb +24 -0
  35. data/lib/masking/config/target_columns/method/type/float.rb +19 -0
  36. data/lib/masking/config/target_columns/method/type/integer.rb +19 -0
  37. data/lib/masking/config/target_columns/method/{null.rb → type/null.rb} +5 -5
  38. data/lib/masking/config/target_columns/method/type/string.rb +37 -0
  39. data/lib/masking/config/target_columns/method/type/time.rb +26 -0
  40. data/lib/masking/config/target_columns/method.rb +14 -10
  41. data/lib/masking/insert_statement/sql_builder.rb +2 -2
  42. data/lib/masking/insert_statement.rb +1 -1
  43. data/lib/masking/sql_dump_line.rb +1 -0
  44. data/lib/masking/version.rb +1 -1
  45. data/masking.gemspec +1 -18
  46. metadata +29 -184
  47. data/lib/masking/cli/error_messages.yml +0 -10
  48. data/lib/masking/config/target_columns/method/binary.rb +0 -23
  49. data/lib/masking/config/target_columns/method/boolean.rb +0 -29
  50. data/lib/masking/config/target_columns/method/date.rb +0 -30
  51. data/lib/masking/config/target_columns/method/float.rb +0 -23
  52. data/lib/masking/config/target_columns/method/integer.rb +0 -23
  53. data/lib/masking/config/target_columns/method/string.rb +0 -33
  54. data/lib/masking/config/target_columns/method/time.rb +0 -28
@@ -1,33 +1,39 @@
1
1
  *************************** 1. row ***************************
2
- id: 1
3
- string: anonymized string
4
- email: anonymized+1@example.com
5
- integer: 12345
6
- float: 123.45
7
- boolean: 1
8
- null: NULL
9
- date: 2018-08-24
10
- time: 2018-08-24 15:54:06
11
- binary_or_blob: \x92
2
+ id: 1
3
+ string: anonymized string
4
+ email: anonymized+1@example.com
5
+ integer: 12345
6
+ float: 123.45
7
+ boolean: 1
8
+ null: NULL
9
+ date: 2018-08-24
10
+ time: 2018-08-24 15:54:06
11
+ binary_or_blob: \x92
12
+ nullable_string: string1
13
+ nullable_integer: NULL
12
14
  *************************** 2. row ***************************
13
- id: 2
14
- string: anonymized string
15
- email: anonymized+2@example.com
16
- integer: 12345
17
- float: 123.45
18
- boolean: 1
19
- null: NULL
20
- date: 2018-08-24
21
- time: 2018-08-24 15:54:06
22
- binary_or_blob: \x92\x92
15
+ id: 2
16
+ string: anonymized string
17
+ email: anonymized+2@example.com
18
+ integer: 12345
19
+ float: 123.45
20
+ boolean: 1
21
+ null: NULL
22
+ date: 2018-08-24
23
+ time: 2018-08-24 15:54:06
24
+ binary_or_blob: \x92\x92
25
+ nullable_string: NULL
26
+ nullable_integer: 33333
23
27
  *************************** 3. row ***************************
24
- id: 3
25
- string: anonymized string
26
- email: anonymized+3@example.com
27
- integer: 12345
28
- float: 123.45
29
- boolean: 1
30
- null: NULL
31
- date: 2018-08-24
32
- time: 2018-08-24 15:54:06
33
- binary_or_blob: NULL
28
+ id: 3
29
+ string: anonymized string
30
+ email: anonymized+3@example.com
31
+ integer: 12345
32
+ float: 123.45
33
+ boolean: 1
34
+ null: NULL
35
+ date: 2018-08-24
36
+ time: 2018-08-24 15:54:06
37
+ binary_or_blob: NULL
38
+ nullable_string: string3
39
+ nullable_integer: 33333
@@ -33,6 +33,8 @@ CREATE TABLE `users` (
33
33
  `date` date DEFAULT NULL,
34
34
  `time` timestamp NULL DEFAULT NULL,
35
35
  `binary_or_blob` binary(11) DEFAULT NULL,
36
+ `nullable_string` varchar(20) DEFAULT '',
37
+ `nullable_integer` int(11) DEFAULT NULL,
36
38
  PRIMARY KEY (`id`)
37
39
  ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
38
40
  /*!40101 SET character_set_client = @saved_cs_client */;
@@ -43,7 +45,7 @@ CREATE TABLE `users` (
43
45
 
44
46
  LOCK TABLES `users` WRITE;
45
47
  /*!40000 ALTER TABLE `users` DISABLE KEYS */;
46
- INSERT INTO `users` (`id`, `string`, `email`, `integer`, `float`, `boolean`, `null`, `date`, `time`, `binary_or_blob`) VALUES (1,'example@exa','test@example.com',1245,2.1,1,-321,NULL,NULL,_binary '\\x92\0\0\0\0\0\0\0'),(2,'あいうえお','invalid@email',0,-23.4422,0,321,NULL,NULL,_binary '\\x92\\x92\0\0\0'),(3,'+-l;a*&^%$','chikahiro@test.com',-1,21.2321,NULL,-231321,'2019-10-31','2019-10-31 16:27:21',NULL);
48
+ INSERT INTO `users` (`id`, `string`, `email`, `integer`, `float`, `boolean`, `null`, `date`, `time`, `binary_or_blob`, `nullable_string`, `nullable_integer`) VALUES (1,'example@exa','test@example.com',1245,2.1,1,-321,NULL,NULL,_binary '\\x92\0\0\0\0\0\0\0','abcde',NULL),(2,'あいうえお','invalid@email',0,-23.4422,0,321,NULL,NULL,_binary '\\x92\\x92\0\0\0',NULL,12),(3,'+-l;a*&^%$','chikahiro@test.com',-1,21.2321,NULL,-231321,'2019-10-31','2019-10-31 16:27:21',NULL,'abc123',-123);
47
49
  /*!40000 ALTER TABLE `users` ENABLE KEYS */;
48
50
  UNLOCK TABLES;
49
51
  /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -7,6 +7,8 @@ users:
7
7
  'null': NULL
8
8
  date: 2018-08-24
9
9
  time: 2018-08-24 15:54:06
10
+ nullable_string?: string%{n}
11
+ nullable_integer?: 33333
10
12
  ## TODO: something not working well with binary...
11
13
  # binary_or_blob: !binary |
12
14
  # R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
@@ -10,16 +10,28 @@ MYSQL_DBNAME=${MYSQL_DBNAME:-mydb}
10
10
 
11
11
  FILEDIR="$( cd "$( dirname "$0" )" && pwd )"
12
12
 
13
- ## clear tmp file
13
+ # clear tmp file
14
14
  rm "$FILEDIR"/tmp/* || echo 'no tmp file'
15
15
 
16
- ## import database
16
+ # import database
17
17
  mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" < "$FILEDIR/import_dumpfile.sql"
18
18
 
19
- ## masking
19
+ # masking & restore
20
20
  mysqldump -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" --complete-insert | exe/masking -c "$FILEDIR/masking.yml" > "$FILEDIR/tmp/masking_dumpfile.sql"
21
21
  mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" < "$FILEDIR/tmp/masking_dumpfile.sql"
22
-
23
- ## compare
22
+ ## compare the result
24
23
  mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" -e 'SELECT * FROM users ORDER BY id;' --vertical > "$FILEDIR/tmp/query_result.txt"
25
- diff "$FILEDIR/tmp/query_result.txt" "$FILEDIR/expected_query_result.txt" && echo 'test passed!'
24
+ diff "$FILEDIR/expected_query_result.txt" "$FILEDIR/tmp/query_result.txt" || (echo 'test failed' && exit 1)
25
+
26
+ # test errors
27
+ set +e
28
+ ## without masking.yml
29
+ mysqldump -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" --complete-insert | exe/masking -c "$FILEDIR/no_file.yml" 2>> "$FILEDIR/tmp/errors.txt" 1> /dev/null
30
+ ## without `--complete-insert``
31
+ mysqldump -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DBNAME" | exe/masking -c "$FILEDIR/masking.yml" 2>> "$FILEDIR/tmp/errors.txt" 1> /dev/null
32
+ set -e
33
+ ### compare the result
34
+ diff "$FILEDIR/expected_error_result.txt" "$FILEDIR/tmp/errors.txt" || (echo 'error output test failed' && exit 1)
35
+
36
+ echo 'test passed!'
37
+ exit 0
@@ -0,0 +1,17 @@
1
+ version: '3.7'
2
+
3
+ services:
4
+ app:
5
+ build:
6
+ target: with-mysql-client
7
+ depends_on:
8
+ - mariadb1010
9
+ entrypoint: docker-compose/wait-for-mysql.sh mariadb1010
10
+
11
+ mariadb1010:
12
+ image: mariadb:10.10
13
+ environment:
14
+ MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
15
+ MYSQL_USER: mysqluser
16
+ MYSQL_PASSWORD: password
17
+ MYSQL_DATABASE: mydb
@@ -0,0 +1,17 @@
1
+ version: '3.7'
2
+
3
+ services:
4
+ app:
5
+ build:
6
+ target: with-mysql-client
7
+ depends_on:
8
+ - mariadb1011
9
+ entrypoint: docker-compose/wait-for-mysql.sh mariadb1011
10
+
11
+ mariadb1011:
12
+ image: mariadb:10.11
13
+ environment:
14
+ MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
15
+ MYSQL_USER: mysqluser
16
+ MYSQL_PASSWORD: password
17
+ MYSQL_DATABASE: mydb
@@ -0,0 +1,17 @@
1
+ version: '3.7'
2
+
3
+ services:
4
+ app:
5
+ build:
6
+ target: with-mysql-client
7
+ depends_on:
8
+ - mariadb105
9
+ entrypoint: docker-compose/wait-for-mysql.sh mariadb105
10
+
11
+ mariadb105:
12
+ image: mariadb:10.5
13
+ environment:
14
+ MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
15
+ MYSQL_USER: mysqluser
16
+ MYSQL_PASSWORD: password
17
+ MYSQL_DATABASE: mydb
@@ -0,0 +1,17 @@
1
+ version: '3.7'
2
+
3
+ services:
4
+ app:
5
+ build:
6
+ target: with-mysql-client
7
+ depends_on:
8
+ - mariadb106
9
+ entrypoint: docker-compose/wait-for-mysql.sh mariadb106
10
+
11
+ mariadb106:
12
+ image: mariadb:10.6
13
+ environment:
14
+ MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
15
+ MYSQL_USER: mysqluser
16
+ MYSQL_PASSWORD: password
17
+ MYSQL_DATABASE: mydb
@@ -0,0 +1,17 @@
1
+ version: '3.7'
2
+
3
+ services:
4
+ app:
5
+ build:
6
+ target: with-mysql-client
7
+ depends_on:
8
+ - mariadb107
9
+ entrypoint: docker-compose/wait-for-mysql.sh mariadb107
10
+
11
+ mariadb107:
12
+ image: mariadb:10.7
13
+ environment:
14
+ MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
15
+ MYSQL_USER: mysqluser
16
+ MYSQL_PASSWORD: password
17
+ MYSQL_DATABASE: mydb
@@ -0,0 +1,17 @@
1
+ version: '3.7'
2
+
3
+ services:
4
+ app:
5
+ build:
6
+ target: with-mysql-client
7
+ depends_on:
8
+ - mariadb108
9
+ entrypoint: docker-compose/wait-for-mysql.sh mariadb108
10
+
11
+ mariadb108:
12
+ image: mariadb:10.8
13
+ environment:
14
+ MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
15
+ MYSQL_USER: mysqluser
16
+ MYSQL_PASSWORD: password
17
+ MYSQL_DATABASE: mydb
@@ -0,0 +1,17 @@
1
+ version: '3.7'
2
+
3
+ services:
4
+ app:
5
+ build:
6
+ target: with-mysql-client
7
+ depends_on:
8
+ - mariadb109
9
+ entrypoint: docker-compose/wait-for-mysql.sh mariadb109
10
+
11
+ mariadb109:
12
+ image: mariadb:10.9
13
+ environment:
14
+ MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
15
+ MYSQL_USER: mysqluser
16
+ MYSQL_PASSWORD: password
17
+ MYSQL_DATABASE: mydb
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
4
3
  require 'erb'
5
4
  require 'ostruct'
5
+ require 'masking/errors'
6
6
 
7
7
  module Masking
8
8
  class Cli
@@ -19,19 +19,26 @@ module Masking
19
19
 
20
20
  attr_reader :error_class
21
21
 
22
- YAML_FILE_PATH = Pathname('lib/masking/cli/error_messages.yml')
23
-
24
- def error_messages
25
- @error_messages = YAML.safe_load(YAML_FILE_PATH.read)
26
- end
27
-
28
22
  def error_message(keyword_args)
29
23
  ERB.new(
30
- error_messages.fetch(error_class.to_s)
24
+ ERROR_MESSAGES.fetch(error_class.to_s)
31
25
  ).result(
32
- OpenStruct.new(keyword_args).instance_eval { binding }
26
+ OpenStruct.new(keyword_args).instance_eval { binding } # rubocop:disable Style/OpenStructUse
33
27
  )
34
28
  end
29
+
30
+ ERROR_MESSAGES = {
31
+ 'Masking::Error::ConfigFileDoesNotExist' =>
32
+ 'ERROR: config file (<%= config_file_path %>) does not exist',
33
+ 'Masking::Error::ConfigFileIsNotFile' =>
34
+ 'ERROR: config file (<%= config_file_path %>) is not file',
35
+ 'Masking::Error::ConfigFileIsNotValidYaml' =>
36
+ 'ERROR: config file (<%= config_file_path %>) is not valid yaml format',
37
+ 'Masking::Error::ConfigFileContainsNullAsColumnName' =>
38
+ 'ERROR: config file (<%= config_file_path %>) is not valid, column name contains `null`',
39
+ 'Masking::Error::InsertStatementParseError' =>
40
+ 'ERROR: cannot parse SQL dump file. you may forget to put `--complete-insert` option in mysqldump?'
41
+ }.freeze
35
42
  end
36
43
  end
37
44
  end
@@ -6,22 +6,36 @@ module Masking
6
6
  class Config
7
7
  class TargetColumns
8
8
  class Column
9
- attr_reader :name, :table_name, :method_value, :method
9
+ attr_reader :table_name, :method_value, :method
10
10
 
11
11
  def initialize(name, table_name:, method_value:)
12
12
  raise ColumnNameIsNil if name.nil?
13
13
 
14
- @name = name.to_sym
15
- @table_name = table_name.to_sym
16
- @method_value = method_value
17
- @method = Method.new(method_value)
14
+ @original_name = name.to_sym
15
+ @table_name = table_name.to_sym
16
+ @method_value = method_value
17
+ @method = Method.new(method_value, ignore_null: ignore_null?)
18
18
  end
19
19
 
20
20
  def ==(other)
21
21
  name == other.name && table_name == other.table_name && method_value == other.method_value
22
22
  end
23
23
 
24
+ def name
25
+ @name ||= ignore_null? ? original_name.to_s.chomp(IGNORE_NULL_SUFFIX).to_sym : original_name
26
+ end
27
+
28
+ def ignore_null?
29
+ @ignore_null ||= original_name.to_s.end_with?(IGNORE_NULL_SUFFIX)
30
+ end
31
+
24
32
  class ColumnNameIsNil < StandardError; end
33
+
34
+ private
35
+
36
+ attr_reader :original_name
37
+
38
+ IGNORE_NULL_SUFFIX = '?'
25
39
  end
26
40
  end
27
41
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'masking/config/target_columns/method/binary'
4
- require 'masking/config/target_columns/method/string'
3
+ require 'masking/config/target_columns/method/type/binary'
4
+ require 'masking/config/target_columns/method/type/string'
5
5
 
6
6
  module Masking
7
7
  class Config
@@ -10,7 +10,7 @@ module Masking
10
10
  module StringBinaryDistinctor
11
11
  class << self
12
12
  def new(value)
13
- binary?(value) ? Binary.new(value) : String.new(value)
13
+ binary?(value) ? Type::Binary.new(value) : Type::String.new(value)
14
14
  end
15
15
 
16
16
  private
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Masking
4
+ class Config
5
+ class TargetColumns
6
+ class Method
7
+ module Type
8
+ class Base
9
+ def initialize(value)
10
+ @value = value
11
+ end
12
+
13
+ def call(_sql_value)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :value
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'masking/config/target_columns/method/type/base'
4
+
5
+ module Masking
6
+ class Config
7
+ class TargetColumns
8
+ class Method
9
+ module Type
10
+ class Binary < Base
11
+ def call(_sql_value)
12
+ "_binary '#{value}'".b
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'masking/config/target_columns/method/type/base'
4
+
5
+ module Masking
6
+ class Config
7
+ class TargetColumns
8
+ class Method
9
+ module Type
10
+ class Boolean < Base
11
+ def call(_sql_value)
12
+ boolean_format.to_s
13
+ end
14
+
15
+ private
16
+
17
+ # NOTE: 11.1.1 Numeric Type Overview, chapter BOOL, BOOLEAN
18
+ # https://dev.mysql.com/doc/refman/8.0/en/numeric-type-overview.html
19
+ def boolean_format
20
+ value ? 1 : 0
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'masking/config/target_columns/method/type/base'
4
+ require 'date'
5
+
6
+ module Masking
7
+ class Config
8
+ class TargetColumns
9
+ class Method
10
+ module Type
11
+ class Date < Base
12
+ def call(_sql_value)
13
+ "'#{date_format}'"
14
+ end
15
+
16
+ private
17
+
18
+ FORMAT = '%Y-%m-%d'
19
+
20
+ def date_format
21
+ value.strftime(FORMAT)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Masking
4
+ class Config
5
+ class TargetColumns
6
+ class Method
7
+ module Type
8
+ module Extension
9
+ module IgnoreNull
10
+ def call(sql_value)
11
+ if sql_value == 'NULL'
12
+ sequence! if respond_to?(:sequence!, true)
13
+ return 'NULL'
14
+ end
15
+
16
+ super
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'masking/config/target_columns/method/type/base'
4
+
5
+ module Masking
6
+ class Config
7
+ class TargetColumns
8
+ class Method
9
+ module Type
10
+ class Float < Base
11
+ def call(_sql_value)
12
+ value.to_s
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'masking/config/target_columns/method/type/base'
4
+
5
+ module Masking
6
+ class Config
7
+ class TargetColumns
8
+ class Method
9
+ module Type
10
+ class Integer < Base
11
+ def call(_sql_value)
12
+ value.to_s
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -4,11 +4,11 @@ module Masking
4
4
  class Config
5
5
  class TargetColumns
6
6
  class Method
7
- class Null
8
- def initialize(*); end
9
-
10
- def call
11
- 'NULL'
7
+ module Type
8
+ class Null < Base
9
+ def call(_sql_value)
10
+ 'NULL'
11
+ end
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'masking/config/target_columns/method/type/base'
4
+
5
+ module Masking
6
+ class Config
7
+ class TargetColumns
8
+ class Method
9
+ module Type
10
+ class String < Base
11
+ def initialize(value)
12
+ super(value)
13
+ @sequence = 0
14
+ end
15
+
16
+ def call(_sql_value)
17
+ sequence!
18
+ "'#{output}'".b
19
+ end
20
+
21
+ private
22
+
23
+ SEQUENTIAL_NUMBER_PLACEHOLDER = '%{n}' # rubocop:disable Style/FormatStringToken
24
+
25
+ def output
26
+ value.sub(SEQUENTIAL_NUMBER_PLACEHOLDER, @sequence.to_s)
27
+ end
28
+
29
+ def sequence!
30
+ @sequence += 1
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'masking/config/target_columns/method/type/base'
4
+
5
+ module Masking
6
+ class Config
7
+ class TargetColumns
8
+ class Method
9
+ module Type
10
+ class Time < Base
11
+ def call(_sql_value)
12
+ "'#{time_format}'"
13
+ end
14
+
15
+ private
16
+
17
+ FORMAT = '%Y-%m-%d %H:%M:%S'
18
+ def time_format
19
+ value.strftime(FORMAT)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'pathname'
4
4
  require 'forwardable'
5
- Dir[Pathname(__FILE__).dirname.join('method/*.rb').to_s].each(&method(:require))
5
+ require 'masking/config/target_columns/method/type/extension/ignore_null'
6
+ require 'masking/config/target_columns/method/string_binary_distinctor'
7
+ Dir[Pathname(__FILE__).dirname.join('method/type/*.rb').to_s].sort.each(&method(:require))
6
8
 
7
9
  module Masking
8
10
  class Config
@@ -10,8 +12,10 @@ module Masking
10
12
  class Method
11
13
  extend Forwardable
12
14
 
13
- def initialize(method)
14
- @method_type = mapping(method.class).new(method)
15
+ def initialize(method, ignore_null: false)
16
+ @method_type = mapping(method.class).new(method).tap do |obj|
17
+ obj.singleton_class.prepend(Type::Extension::IgnoreNull) if ignore_null
18
+ end
15
19
  end
16
20
 
17
21
  def_delegator :@method_type, :call
@@ -21,13 +25,13 @@ module Masking
21
25
  # rubocop:disable Layout/HashAlignment
22
26
  MAPPING = {
23
27
  ::String => StringBinaryDistinctor,
24
- ::Integer => Integer,
25
- ::Float => Float,
26
- ::Date => Date,
27
- ::Time => Time,
28
- ::TrueClass => Boolean,
29
- ::FalseClass => Boolean,
30
- ::NilClass => Null
28
+ ::Integer => Type::Integer,
29
+ ::Float => Type::Float,
30
+ ::Date => Type::Date,
31
+ ::Time => Type::Time,
32
+ ::TrueClass => Type::Boolean,
33
+ ::FalseClass => Type::Boolean,
34
+ ::NilClass => Type::Null
31
35
  }.freeze
32
36
  # rubocop:enable Layout/HashAlignment
33
37