lexicon-common 0.1.0 → 0.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb97bec1598709b8fefb46c28055ac8291edd138177995e08616de3b06dfbdb8
4
- data.tar.gz: 20db194da73907714cb53ae861bd650ded613f71ef99ae1c93db370c8547c830
3
+ metadata.gz: 5b24914e5f9dbd3d4261ec1b6c04741958a5d9fcfccb9dde8fd14b92b5c19b57
4
+ data.tar.gz: 7b2a40b0e4e033dbc111804a5c6a6a7105fdd2ac991d72baeb7022e66f585913
5
5
  SHA512:
6
- metadata.gz: aa5b4f83c9647382dabdaa4dd09250fd4e165476ca3984740763bd4b2cd8f54ac41e73379a5988391a3336d31e9928ca9332598216b102829dff1efe28ab14dd
7
- data.tar.gz: 296628e759a78533b06160d653ab6e1299d0d1378964ac20d5350c7b83892d0b385b4d78f1569c1844591c014db2a0130f9249e423946e7d3e38095f28029f5a
6
+ metadata.gz: 0b7e024f856d40571826aa8237cfbd0b1ea55be19bf31f997ce5f65cf04c561ffaa43d4b5c23b5fcdda05ae7f3b48629ae0ca68c0c91903fa362e4674e3cdbd4
7
+ data.tar.gz: 7c225eb0b67789074fa76e5a50bcc61439503b5fb579d811077732b7f9763efc822f7f4c12d2aa314d5b0719ef3820d0eff097e08aaa3c167d442f48665dbe64
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency 'aws-sdk-s3', '~> 1.84'
22
22
  spec.add_dependency 'colored', '~> 1.2'
23
+ spec.add_dependency 'concurrent-ruby', '~> 1.1'
24
+ spec.add_dependency 'corindon', '~> 0.8.0'
23
25
  spec.add_dependency 'json_schemer', '~> 0.2.16'
24
26
  spec.add_dependency 'pg', '~> 1.2'
25
27
  spec.add_dependency 'semantic', '~> 1.6'
@@ -50,7 +50,7 @@ module Lexicon
50
50
  drop_schema(schema, cascade: true)
51
51
  end
52
52
 
53
- def drop_schema(name, cascade: false)
53
+ def drop_schema(name, cascade: false, if_exists: false)
54
54
  cascade = if cascade
55
55
  ' CASCADE'
56
56
  else
@@ -58,7 +58,7 @@ module Lexicon
58
58
  end
59
59
 
60
60
  query <<~SQL
61
- DROP SCHEMA "#{name}"#{cascade};
61
+ DROP SCHEMA #{if_exists ? 'IF EXISTS ' : ''}"#{name}"#{cascade};
62
62
  SQL
63
63
  end
64
64
 
@@ -95,6 +95,28 @@ module Lexicon
95
95
  @connection.copy_data(sql) { block.call(put_data) }
96
96
  end
97
97
 
98
+ # @param [#to_s] table
99
+ # @param [#to_s | nil] schema
100
+ # @return [Boolean]
101
+ def table_exists?(table, schema: nil)
102
+ schema = search_path.first if schema.nil?
103
+
104
+ query(<<~SQL, table, schema).any?
105
+ SELECT table_name FROM information_schema.tables
106
+ WHERE table_name = $1 AND table_schema = $2
107
+ SQL
108
+ end
109
+
110
+ # @param [#to_s] schema_name
111
+ # @return [Boolean]
112
+ def schema_exists?(schema_name)
113
+ query(<<~SQL, schema_name).count > 0
114
+ SELECT "schema_name"
115
+ FROM "information_schema"."schemata"
116
+ WHERE "schema_name" = $1
117
+ SQL
118
+ end
119
+
98
120
  private
99
121
 
100
122
  # @return [PG::Connection]
@@ -16,8 +16,8 @@ module Lexicon
16
16
  def log_error(error)
17
17
  if error.nil?
18
18
  log('Error (nil)')
19
- else
20
- log([error.message, *error.backtrace].join("\n"))
19
+ elsif !logger.nil?
20
+ logger.error([error.message, *error.backtrace].join("\n"))
21
21
  end
22
22
  end
23
23
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Corindon::Guards::Ext
4
+
5
+ module Lexicon
6
+ module Common
7
+ module Mixin
8
+ module Nameable
9
+ # @return [String]
10
+ def name
11
+ unimplemented!
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -17,7 +17,7 @@ module Lexicon
17
17
  end
18
18
 
19
19
  # @param [String] name
20
- # @return [Package, nil]
20
+ # @return [Package::Package, nil]
21
21
  def load_package(name)
22
22
  package_dir = root_dir.join(name.to_s)
23
23
 
@@ -40,18 +40,17 @@ module Lexicon
40
40
  json = JSON.parse(spec_file.read)
41
41
 
42
42
  if @schema_validator.valid?(json)
43
- version = Semantic::Version.new(json.fetch('version'))
44
- file_sets = json.fetch('content').map do |id, values|
45
- SourceFileSet.new(
46
- id: id,
47
- name: values.fetch('name'),
48
- structure: values.fetch('structure'),
49
- data: values.fetch('data', nil),
50
- tables: values.fetch('tables', [])
51
- )
52
- end
43
+ package_version = json.fetch('schema_version', 1)
44
+ case package_version
45
+ when 1
46
+ load_v1(dir: dir, spec_file: spec_file, checksum_file: checksum_file, json: json)
47
+ when 2
48
+ load_v2(dir: dir, spec_file: spec_file, checksum_file: checksum_file, json: json)
49
+ else
50
+ log("Package version #{package_version} is not supported")
53
51
 
54
- Package.new(file_sets: file_sets, version: version, dir: dir, checksum_file: checksum_file, spec_file: spec_file)
52
+ nil
53
+ end
55
54
  else
56
55
  log("Package at path #{dir} has invalid manifest")
57
56
 
@@ -61,6 +60,51 @@ module Lexicon
61
60
  nil
62
61
  end
63
62
  end
63
+
64
+ # @param [Pathname] checksum_file
65
+ # @param [Pathname] dir
66
+ # @param [Hash] json
67
+ # @param [Pathname] spec_file
68
+ # @return [V1::Package]
69
+ def load_v1(dir:, spec_file:, checksum_file:, json:)
70
+ version = Semantic::Version.new(json.fetch('version'))
71
+ file_sets = json.fetch('content').map do |id, values|
72
+ V1::SourceFileSet.new(
73
+ id: id,
74
+ name: values.fetch('name'),
75
+ structure: values.fetch('structure'),
76
+ data: values.fetch('data', nil),
77
+ tables: values.fetch('tables', [])
78
+ )
79
+ end
80
+
81
+ V1::Package.new(file_sets: file_sets, version: version, dir: dir, checksum_file: checksum_file, spec_file: spec_file)
82
+ end
83
+
84
+ # @param [Pathname] checksum_file
85
+ # @param [Pathname] dir
86
+ # @param [Hash] json
87
+ # @param [Pathname] spec_file
88
+ # @return [V2::Package]
89
+ def load_v2(dir:, spec_file:, checksum_file:, json:)
90
+ version = Semantic::Version.new(json.fetch('version'))
91
+ file_sets = json.fetch('content').map do |id, values|
92
+ V2::SourceFileSet.new(
93
+ id: id,
94
+ name: values.fetch('name'),
95
+ structure: values.fetch('structure'),
96
+ tables: values.fetch('tables', {})
97
+ )
98
+ end
99
+
100
+ V2::Package.new(
101
+ file_sets: file_sets,
102
+ version: version,
103
+ dir: dir,
104
+ checksum_file: checksum_file,
105
+ spec_file: spec_file
106
+ )
107
+ end
64
108
  end
65
109
  end
66
110
  end
@@ -11,65 +11,33 @@ module Lexicon
11
11
  attr_reader :checksum_file, :spec_file
12
12
  # @return [Pathname]
13
13
  attr_reader :dir
14
- # @return [Array<SourceFileSet>]
15
- attr_reader :file_sets
16
14
  # @return [Semantic::Version]
17
15
  attr_reader :version
16
+ # @return [Integer]
17
+ attr_reader :schema_version
18
18
 
19
- # @param [Array<SourceFileSet>] file_sets
20
- # @param [Pathname] dir
21
19
  # @param [Pathname] checksum_file
20
+ # @param [Pathname] dir
21
+ # @param [Integer] schema_version
22
+ # @param [Pathname] spec_file
22
23
  # @param [Semantic::Version] version
23
- def initialize(file_sets:, version:, dir:, checksum_file:, spec_file:)
24
+ def initialize(checksum_file:, dir:, schema_version:, spec_file:, version:)
24
25
  @checksum_file = checksum_file
25
26
  @dir = dir
26
- @file_sets = file_sets
27
+ @schema_version = schema_version
27
28
  @spec_file = spec_file
28
29
  @version = version
29
30
  end
30
31
 
31
32
  # @return [Boolean]
32
33
  def valid?
33
- checksum_file.exist? && dir.directory? && data_dir.directory? && all_sets_valid?
34
- end
35
-
36
- # @return [Array<Pathname>]
37
- def structure_files
38
- file_sets.map { |fs| dir.join(relative_structure_path(fs)) }
34
+ checksum_file.exist? && dir.directory? && files.all? { |f| f.path.exist? rescue false }
39
35
  end
40
36
 
41
- # @param [SourceFileSet] file_set
42
- # @return [Pathname]
43
- def data_path(file_set)
44
- dir.join(relative_data_path(file_set))
45
- end
46
-
47
- # @return [Pathname]
48
- def data_dir
49
- dir.join('data')
37
+ # @return [Array<PackageFile>] Array of File of the package
38
+ def files
39
+ []
50
40
  end
51
-
52
- # @return [Pathname, nil]
53
- def relative_data_path(file_set)
54
- if file_set.data_path.nil?
55
- nil
56
- else
57
- data_dir.basename.join(file_set.data_path)
58
- end
59
- end
60
-
61
- # @return [Pathname]
62
- def relative_structure_path(file_set)
63
- data_dir.basename.join(file_set.structure_path)
64
- end
65
-
66
- private
67
-
68
- def all_sets_valid?
69
- file_sets.all? do |set|
70
- data_dir.join(set.structure_path).exist? && !set.data_path.nil? && data_dir.join(set.data_path).exist?
71
- end
72
- end
73
41
  end
74
42
  end
75
43
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexicon
4
+ module Common
5
+ module Package
6
+ class PackageFile
7
+ class << self
8
+ def new_structure(path)
9
+ new(path, type: STRUCTURE)
10
+ end
11
+
12
+ def new_data(path)
13
+ new(path, type: DATA)
14
+ end
15
+ end
16
+
17
+ # @return [Pathname]
18
+ attr_reader :path
19
+
20
+ # @return [String]
21
+ def to_s
22
+ path.to_s
23
+ end
24
+
25
+ # @return [Boolean]
26
+ def data?
27
+ type == DATA
28
+ end
29
+
30
+ # @return [Boolean]
31
+ def structure?
32
+ type == STRUCTURE
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :type
38
+
39
+ DATA = 'data'
40
+ STRUCTURE = 'structure'
41
+
42
+ def initialize(path, type:)
43
+ @path = path
44
+ @type = type
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexicon
4
+ module Common
5
+ module Package
6
+ module V1
7
+ class Package < Common::Package::Package
8
+ # @return [Array<SourceFileSet>]
9
+ attr_reader :file_sets
10
+
11
+ # @param [Array<SourceFileSet>] file_sets
12
+ # @param [Pathname] dir
13
+ # @param [Pathname] checksum_file
14
+ # @param [Semantic::Version] version
15
+ def initialize(version:, spec_file:, checksum_file:, dir:, file_sets:)
16
+ super(
17
+ checksum_file: checksum_file,
18
+ dir: dir,
19
+ spec_file: spec_file,
20
+ schema_version: 1,
21
+ version: version,
22
+ )
23
+
24
+ @file_sets = file_sets
25
+ end
26
+
27
+ # @return [Boolean]
28
+ def valid?
29
+ super && data_dir.directory? && all_sets_valid?
30
+ end
31
+
32
+ def files
33
+ structures = file_sets.map { |fs| PackageFile.new_structure(relative_structure_path(fs)) }
34
+ data = file_sets.flat_map do |fs|
35
+ data_path = relative_data_path(fs)
36
+
37
+ if data_path.nil?
38
+ []
39
+ else
40
+ [PackageFile.new_data(data_path)]
41
+ end
42
+ end
43
+
44
+ [*structures, *data]
45
+ end
46
+
47
+ # @param [SourceFileSet] file_set
48
+ # @return [Pathname]
49
+ def data_path(file_set)
50
+ dir.join(relative_data_path(file_set))
51
+ end
52
+
53
+ # @return [Pathname]
54
+ def data_dir
55
+ dir.join('data')
56
+ end
57
+
58
+ # @return [Pathname, nil]
59
+ def relative_data_path(file_set)
60
+ if file_set.data_path.nil?
61
+ nil
62
+ else
63
+ data_dir.basename.join(file_set.data_path)
64
+ end
65
+ end
66
+
67
+ # @return [Pathname]
68
+ def relative_structure_path(file_set)
69
+ data_dir.basename.join(file_set.structure_path)
70
+ end
71
+
72
+ private
73
+
74
+ def all_sets_valid?
75
+ file_sets.all? do |set|
76
+ data_dir.join(set.structure_path).exist? && !set.data_path.nil? && data_dir.join(set.data_path).exist?
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexicon
4
+ module Common
5
+ module Package
6
+ module V1
7
+ class PackageBuilder < Package
8
+ def initialize(version:, dir:)
9
+ super(
10
+ file_sets: [],
11
+ version: version,
12
+ dir: dir,
13
+ checksum_file: dir.join(CHECKSUM_FILE_NAME),
14
+ spec_file: dir.join(SPEC_FILE_NAME)
15
+ )
16
+
17
+ FileUtils.mkdir_p(data_dir)
18
+ end
19
+
20
+ # @param [String] id
21
+ # @param [String] name
22
+ # @param [Pathname] structure
23
+ # Takes ownership of the file (moves it to the correct folder)
24
+ # @param [Array<String>] tables
25
+ # @param [Pathname] data
26
+ # Takes ownership of the file (moves it to the correct folder)
27
+ # @param [String] data_ext
28
+ def add_file_set(id, name:, structure:, tables:, data: nil, data_ext: '.sql')
29
+ # @type [Pathname] structure_file_path
30
+ structure_file_path = data_dir.join(structure_file_name(id))
31
+ FileUtils.mv(structure.to_s, structure_file_path.to_s)
32
+
33
+ # @type [Pathname] data_file_path
34
+ data_name = if data.nil?
35
+ nil
36
+ else
37
+ dname = data_file_name(id, data_ext)
38
+ path = data_dir.join(dname)
39
+ FileUtils.mv(data, path)
40
+
41
+ dname
42
+ end
43
+
44
+ file_sets << SourceFileSet.new(
45
+ id: id,
46
+ name: name,
47
+ structure: structure_file_name(id),
48
+ data: data.nil? ? nil : data_name,
49
+ tables: tables
50
+ )
51
+ end
52
+
53
+ def as_package
54
+ Package.new(version: version, dir: dir, file_sets: file_sets, checksum_file: checksum_file, spec_file: spec_file)
55
+ end
56
+
57
+ private
58
+
59
+ def data_file_name(id, ext)
60
+ "#{id}#{ext}"
61
+ end
62
+
63
+ def structure_file_name(id)
64
+ "#{id}__structure.sql"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexicon
4
+ module Common
5
+ module Package
6
+ module V1
7
+ class SourceFileSet
8
+ include Mixin::Nameable
9
+
10
+ attr_reader :id, :name, :structure_path, :data_path, :tables
11
+
12
+ # @param [String] id
13
+ # @param [String] name
14
+ # @param [String] structure
15
+ # @param [String] data
16
+ # @param [Array<String>] tables
17
+ def initialize(id:, name:, structure:, data:, tables:)
18
+ @id = id
19
+ @name = name
20
+ @structure_path = structure
21
+ @data_path = data
22
+ @tables = tables
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexicon
4
+ module Common
5
+ module Package
6
+ module V2
7
+ class Package < Common::Package::Package
8
+ # @param [Pathname] dir
9
+ # @param [Pathname] checksum_file
10
+ # @param [Semantic::Version] version
11
+ def initialize(version:, spec_file:, checksum_file:, dir:, file_sets:)
12
+ super(
13
+ checksum_file: checksum_file,
14
+ dir: dir,
15
+ spec_file: spec_file,
16
+ schema_version: 2,
17
+ version: version,
18
+ )
19
+
20
+ @file_sets = file_sets
21
+ end
22
+
23
+ def valid?
24
+ super
25
+ end
26
+
27
+ def files
28
+ file_sets.flat_map { |fs| file_set_files(fs) }
29
+ end
30
+
31
+ # @return [SourceFileSet]
32
+ attr_reader :file_sets
33
+
34
+ def data_dir
35
+ dir.join('data')
36
+ end
37
+
38
+ private
39
+
40
+ # @param [SourceFileSet] file_set
41
+ # @return [Array<PackageFile>]
42
+ def file_set_files(file_set)
43
+ relative_data_dir = data_dir.basename
44
+
45
+ structure_file = PackageFile.new_structure(relative_data_dir.join(file_set.structure))
46
+ table_files = file_set.tables
47
+ .values.flatten(1)
48
+ .map { |table_file| PackageFile.new_data(relative_data_dir.join(table_file)) }
49
+
50
+ [structure_file, *table_files]
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexicon
4
+ module Common
5
+ module Package
6
+ module V2
7
+ class PackageBuilder < Package
8
+ def initialize(version:, dir:)
9
+ super(
10
+ file_sets: [],
11
+ version: version,
12
+ dir: dir,
13
+ checksum_file: dir.join(CHECKSUM_FILE_NAME),
14
+ spec_file: dir.join(SPEC_FILE_NAME),
15
+ )
16
+
17
+ FileUtils.mkdir_p(data_dir)
18
+ end
19
+
20
+ # @param [String] id
21
+ # @param [String] name
22
+ # @param [Pathname] structure
23
+ # Takes ownership of the file (moves it to the correct folder)
24
+ # @param [Hash{String=>Array<Pathname>}] tables
25
+ # Takes ownership of the files (moves them to the correct folder)
26
+ def add_file_set(id, name:, structure:, tables:)
27
+ # @type [Pathname] structure_file_path
28
+ structure_file_path = data_dir.join(structure_file_name(id))
29
+ FileUtils.mv(structure.to_s, structure_file_path.to_s)
30
+
31
+ table_data = tables.map do |table_name, files|
32
+ index = 0
33
+
34
+ file_names = files.map do |file|
35
+ file_name = "#{table_name}_#{index}.csv.gz"
36
+ FileUtils.mv(file.to_s, data_dir.join(file_name))
37
+ index += 1
38
+
39
+ file_name
40
+ end
41
+
42
+ [table_name, file_names]
43
+ end
44
+
45
+ file_sets << SourceFileSet.new(
46
+ id: id,
47
+ name: name,
48
+ structure: structure_file_name(id),
49
+ tables: table_data.to_h
50
+ )
51
+ end
52
+
53
+ def as_package
54
+ Package.new(
55
+ checksum_file: checksum_file,
56
+ dir: dir,
57
+ file_sets: file_sets,
58
+ spec_file: spec_file,
59
+ version: version,
60
+ )
61
+ end
62
+
63
+ private
64
+
65
+ def structure_file_name(id)
66
+ "#{id}__structure.sql"
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexicon
4
+ module Common
5
+ module Package
6
+ module V2
7
+ class SourceFileSet
8
+ include Mixin::Nameable
9
+
10
+ attr_reader :id, :name, :structure, :tables
11
+
12
+ # @param [String] id
13
+ # @param [String] name
14
+ # @param [String] structure
15
+ # @param [Hash{String=>Array<String>}] tables
16
+ def initialize(id:, name:, structure:, tables:)
17
+ @id = id
18
+ @name = name
19
+ @structure = structure
20
+ @tables = tables.freeze
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end