masking 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/masking/cli.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'masking/config'
4
+ require 'masking/version'
4
5
  require 'masking/errors'
5
6
  require 'masking/cli/error_message'
6
7
  require 'optparse'
@@ -14,8 +15,8 @@ module Masking
14
15
  def run
15
16
  option_parser.parse(argv)
16
17
  Masking.run
17
- rescue Masking::Error => error
18
- warn(Masking::Cli::ErrorMessage.new(error).message(config_file_path: Masking.config.target_columns_file_path))
18
+ rescue Masking::Error => e
19
+ warn(Masking::Cli::ErrorMessage.new(e).message(config_file_path: Masking.config.target_columns_file_path))
19
20
  exit(false)
20
21
  end
21
22
 
@@ -6,8 +6,7 @@ module Masking
6
6
  class Config
7
7
  class TargetColumns
8
8
  class Column
9
- attr_reader :name, :table_name, :method_value
10
- attr_accessor :index
9
+ attr_reader :name, :table_name, :method_value, :method
11
10
 
12
11
  def initialize(name, table_name:, method_value:)
13
12
  raise ColumnNameIsNil if name.nil?
@@ -18,17 +17,10 @@ module Masking
18
17
  @method = Method.new(method_value)
19
18
  end
20
19
 
21
- def masked_value
22
- method.call
23
- end
24
-
25
20
  def ==(other)
26
21
  name == other.name && table_name == other.table_name && method_value == other.method_value
27
22
  end
28
23
 
29
- private
30
-
31
- attr_reader :method
32
24
  class ColumnNameIsNil < StandardError; end
33
25
  end
34
26
  end
@@ -18,7 +18,7 @@ module Masking
18
18
 
19
19
  private
20
20
 
21
- # rubocop:disable Layout/AlignHash
21
+ # rubocop:disable Layout/HashAlignment
22
22
  MAPPING = {
23
23
  ::String => StringBinaryDistinctor,
24
24
  ::Integer => Integer,
@@ -29,7 +29,7 @@ module Masking
29
29
  ::FalseClass => Boolean,
30
30
  ::NilClass => Null
31
31
  }.freeze
32
- # rubocop:enable Layout/AlignHash
32
+ # rubocop:enable Layout/HashAlignment
33
33
 
34
34
  def mapping(klass)
35
35
  MAPPING[klass] || raise(UnknownType)
@@ -34,7 +34,7 @@ module Masking
34
34
  private
35
35
 
36
36
  def data
37
- @data ||= YAML.safe_load(file_path.read, [Date, Time])
37
+ @data ||= YAML.safe_load(file_path.read, permitted_classes: [Date, Time])
38
38
  rescue Psych::SyntaxError
39
39
  raise Masking::Error::ConfigFileIsNotValidYaml
40
40
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Masking
4
+ class DataMaskProcessor
5
+ class Cache
6
+ def self.fetch_or_store_if_no_cache(table:, proc:)
7
+ @cache ||= {}
8
+
9
+ if @cache.key?(table)
10
+ @cache[table]
11
+ else
12
+ @cache[table] = proc.call
13
+ end
14
+ end
15
+
16
+ # onlu for test
17
+ def self.clear
18
+ @cache = {}
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,51 +1,59 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'masking/data_mask_processor/cache'
3
4
  require 'masking/config/target_columns'
4
5
  require 'masking/insert_statement'
5
6
 
6
7
  module Masking
7
- # TODO: find better naming/modeling of DataMaskProcessor
8
8
  class DataMaskProcessor
9
- class << self
10
- def process(insert_statement_line, target_columns: ::Masking.config.target_columns)
11
- new(insert_statement_line, target_columns: target_columns).send(:process)
12
- end
13
- end
14
-
15
- private
16
-
17
- attr_reader :raw_line, :target_columns, :insert_statement
18
-
19
- def initialize(insert_statement_line, target_columns:)
9
+ def initialize(
10
+ insert_statement_line,
11
+ target_columns: ::Masking.config.target_columns,
12
+ insert_statement: InsertStatement.new(insert_statement_line),
13
+ cache_store: Cache
14
+ )
20
15
  @raw_line = insert_statement_line
21
16
  @target_columns = target_columns
22
- @insert_statement = InsertStatement.new(insert_statement_line)
17
+ @insert_statement = insert_statement
18
+ @cache_store = cache_store
23
19
  end
24
20
 
25
- # TODO: define insert_statement.mask_values(column, mask_method) method & refactoring
26
- # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
27
21
  def process
28
22
  return raw_line unless target_table?
29
23
 
30
- columns = target_columns.columns(table_name: insert_statement.table)
31
- if columns.first.index.nil?
32
- columns.each do |target_column|
33
- target_column.index = insert_statement.columns.index(target_column.name)
34
- end
35
- end
24
+ column_indexes_mask_methods.each do |column_index, mask_method|
25
+ next if column_index.nil?
36
26
 
37
- insert_statement.values.each do |values|
38
- columns.each do |target_column|
39
- values[target_column.index] = target_column.masked_value unless target_column.index.nil?
40
- end
27
+ insert_statement.mask_value(
28
+ column_index: column_index,
29
+ mask_method: mask_method
30
+ )
41
31
  end
42
32
 
43
33
  insert_statement.sql
44
34
  end
45
- # rubocop:enable Metrics/AbcSize,Metrics/MethodLength
35
+
36
+ private
37
+
38
+ attr_reader :raw_line, :target_columns, :insert_statement, :cache_store
46
39
 
47
40
  def target_table?
48
- target_columns.contains?(table_name: insert_statement.table)
41
+ target_columns.contains?(table_name: table_name)
42
+ end
43
+
44
+ def column_indexes_mask_methods
45
+ cache_store.fetch_or_store_if_no_cache(
46
+ table: table_name,
47
+ proc: proc {
48
+ target_columns.columns(table_name: table_name).map do |column|
49
+ [insert_statement.column_index(column.name), column.method]
50
+ end
51
+ }
52
+ )
53
+ end
54
+
55
+ def table_name
56
+ @table_name = insert_statement.table
49
57
  end
50
58
  end
51
59
  end
@@ -3,25 +3,20 @@
3
3
  module Masking
4
4
  class InsertStatement
5
5
  class SQLBuilder
6
- class << self
7
- def build(table:, columns:, values:)
8
- new(table: table, columns: columns, values: values).send(:build)
9
- end
10
- end
11
-
12
- private
13
-
14
- attr_reader :table, :columns, :values
15
6
  def initialize(table:, columns:, values:)
16
7
  @table = table
17
8
  @columns = columns
18
9
  @values = values
19
10
  end
20
11
 
21
- def build
12
+ def sql
22
13
  %(INSERT INTO `#{table}` #{columns_section} VALUES #{values_section};\n)
23
14
  end
24
15
 
16
+ private
17
+
18
+ attr_reader :table, :columns, :values
19
+
25
20
  def columns_section
26
21
  '(' + columns.map { |column| "`#{column}`" }.join(', ') + ')'
27
22
  end
@@ -7,8 +7,9 @@ module Masking
7
7
  class InsertStatement
8
8
  attr_reader :raw_statement, :table
9
9
 
10
- def initialize(raw_statement)
10
+ def initialize(raw_statement, sql_builder: SQLBuilder)
11
11
  @raw_statement = raw_statement
12
+ @sql_builder = sql_builder
12
13
 
13
14
  PARSE_REGEXP.match(raw_statement).tap do |match_data|
14
15
  raise Error::InsertStatementParseError if match_data.nil?
@@ -20,24 +21,32 @@ module Masking
20
21
  end
21
22
 
22
23
  def columns
23
- # NOTE: define and extract to ColumnSet class?
24
24
  @columns ||= columns_section.scan(COLUMNS_REGEXP).flatten.map(&:to_sym)
25
25
  end
26
26
 
27
+ def column_index(column_name)
28
+ columns.index(column_name)
29
+ end
30
+
27
31
  def values
28
- # NOTE: define and extract to ValueSet class?
29
32
  @values ||= values_section.split(VALUE_ROW_SPLITTER)
30
33
  .tap { |rows| rows.each_with_index { |_, i| recursive_pattern_value_concat(rows, i) } }
31
34
  .flat_map { |row| row.scan(values_regexp) }
32
35
  end
33
36
 
37
+ def mask_value(column_index:, mask_method:)
38
+ values.each do |value|
39
+ value[column_index] = mask_method.call
40
+ end
41
+ end
42
+
34
43
  def sql
35
- SQLBuilder.build(table: table, columns: columns, values: values)
44
+ sql_builder.new(table: table, columns: columns, values: values).sql
36
45
  end
37
46
 
38
47
  private
39
48
 
40
- attr_reader :columns_section, :values_section
49
+ attr_reader :columns_section, :values_section, :sql_builder
41
50
 
42
51
  VALUE_ROW_SPLITTER = '),('
43
52
  PARSE_REGEXP = /INSERT INTO `(?<table>.+)` \((?<columns_section>.+)\) VALUES (?<values_section>.+);/.freeze
@@ -4,21 +4,36 @@ require 'masking/data_mask_processor'
4
4
 
5
5
  module Masking
6
6
  class SQLDumpLine
7
- def initialize(line)
7
+ def initialize(line, mask_processor: DataMaskProcessor)
8
8
  @line = line
9
+ @mask_processor = mask_processor
9
10
  end
10
11
 
11
- def output
12
- insert_statement? ? DataMaskProcessor.process(line) : line
12
+ def mask
13
+ processor.new(line).process
14
+ end
15
+
16
+ def insert_statement?
17
+ line.match?(INSERT_STATEMENT_REGEXP)
13
18
  end
14
19
 
15
20
  private
16
21
 
17
- attr_reader :line
22
+ attr_reader :line, :mask_processor
18
23
  INSERT_STATEMENT_REGEXP = /^INSERT/.freeze
19
24
 
20
- def insert_statement?
21
- line.match?(INSERT_STATEMENT_REGEXP)
25
+ def processor
26
+ insert_statement? ? mask_processor : NoMaskProcessor
27
+ end
28
+
29
+ class NoMaskProcessor
30
+ def initialize(line)
31
+ @line = line
32
+ end
33
+
34
+ def process
35
+ @line # do nothing
36
+ end
22
37
  end
23
38
  end
24
39
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Masking
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/masking.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'masking/version'
4
3
  require 'masking/cli'
5
4
  require 'masking/config'
6
5
  require 'masking/sql_dump_line'
@@ -13,19 +12,20 @@ module Masking
13
12
  end
14
13
 
15
14
  class Main
16
- def initialize(input: $stdin, output: $stdout)
15
+ def initialize(input: $stdin, output: $stdout, line_processor: SQLDumpLine)
17
16
  @input = input.set_encoding(Encoding::ASCII_8BIT, Encoding::ASCII_8BIT)
18
17
  @output = output.set_encoding(Encoding::ASCII_8BIT, Encoding::ASCII_8BIT)
18
+ @line_processor = line_processor
19
19
  end
20
20
 
21
21
  def run
22
22
  input.each_line do |line|
23
- output.print SQLDumpLine.new(line).output
23
+ output.print line_processor.new(line).mask
24
24
  end
25
25
  end
26
26
 
27
27
  private
28
28
 
29
- attr_reader :input, :output
29
+ attr_reader :input, :output, :line_processor
30
30
  end
31
31
  end
data/masking.gemspec CHANGED
@@ -23,10 +23,10 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ['lib']
25
25
 
26
- spec.required_ruby_version = '>= 2.5'
26
+ spec.required_ruby_version = '>= 2.6'
27
27
 
28
- spec.add_development_dependency 'bundler', '~> 2.0'
29
- spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'bundler'
29
+ spec.add_development_dependency 'rake'
30
30
  spec.add_development_dependency 'rake-notes'
31
31
  spec.add_development_dependency 'ruby-prof'
32
32
 
@@ -35,8 +35,8 @@ Gem::Specification.new do |spec|
35
35
  spec.add_development_dependency 'rubocop'
36
36
 
37
37
  # test
38
- spec.add_development_dependency 'coveralls'
39
- spec.add_development_dependency 'rspec', '~> 3.0'
38
+ spec.add_development_dependency 'codecov'
39
+ spec.add_development_dependency 'rspec'
40
40
  spec.add_development_dependency 'simplecov'
41
41
 
42
42
  # debug
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: masking
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chikahiro Tokoro
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-10 00:00:00.000000000 Z
11
+ date: 2023-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '0'
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: '10.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake-notes
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: coveralls
98
+ name: codecov
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -112,16 +112,16 @@ dependencies:
112
112
  name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - "~>"
115
+ - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: '3.0'
117
+ version: '0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - "~>"
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '3.0'
124
+ version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: simplecov
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -210,7 +210,9 @@ files:
210
210
  - acceptance/masking.yml
211
211
  - acceptance/run_test.sh
212
212
  - acceptance/tmp/.keep
213
- - bin/benchmark.rb
213
+ - benchmark/masking.yml
214
+ - benchmark/run.rb
215
+ - benchmark/users.sql
214
216
  - bin/console
215
217
  - bin/masking_profile
216
218
  - bin/setup
@@ -250,6 +252,7 @@ files:
250
252
  - lib/masking/config/target_columns/method/time.rb
251
253
  - lib/masking/config/target_columns/table.rb
252
254
  - lib/masking/data_mask_processor.rb
255
+ - lib/masking/data_mask_processor/cache.rb
253
256
  - lib/masking/errors.rb
254
257
  - lib/masking/insert_statement.rb
255
258
  - lib/masking/insert_statement/sql_builder.rb
@@ -263,7 +266,7 @@ homepage: https://github.com/kibitan/masking
263
266
  licenses:
264
267
  - MIT
265
268
  metadata: {}
266
- post_install_message:
269
+ post_install_message:
267
270
  rdoc_options: []
268
271
  require_paths:
269
272
  - lib
@@ -271,15 +274,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
271
274
  requirements:
272
275
  - - ">="
273
276
  - !ruby/object:Gem::Version
274
- version: '2.5'
277
+ version: '2.6'
275
278
  required_rubygems_version: !ruby/object:Gem::Requirement
276
279
  requirements:
277
280
  - - ">="
278
281
  - !ruby/object:Gem::Version
279
282
  version: '0'
280
283
  requirements: []
281
- rubygems_version: 3.0.3
282
- signing_key:
284
+ rubygems_version: 3.3.26
285
+ signing_key:
283
286
  specification_version: 4
284
287
  summary: Command line tool for anonymizing databese records
285
288
  test_files: []