manifold-cli 0.0.7 → 0.0.8
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 +4 -4
- data/.rubocop.yml +2 -1
- data/bin/manifold +1 -1
- data/lib/manifold/api/project.rb +44 -0
- data/lib/manifold/api/vector.rb +34 -0
- data/lib/manifold/api/workspace.rb +104 -0
- data/lib/manifold/cli.rb +11 -15
- data/lib/manifold/services/vector_service.rb +4 -5
- data/lib/manifold/version.rb +1 -1
- data/lib/manifold.rb +3 -1
- metadata +7 -7
- data/lib/manifold/project/project.rb +0 -33
- data/lib/manifold/project/vector.rb +0 -37
- data/lib/manifold/project/workspace.rb +0 -51
- data/lib/manifold/services/big_query_service.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0478f04fda1c307a346fb2e0a58f93949e73fadbf533b10d1bc374d3c9dd6b3
|
4
|
+
data.tar.gz: b6b8fcd1cdc6291af4a01b2014d61257f7b4ac585f54e1f93beaabfc31b5a511
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb8477bd304089eb1d48c5b2f276333b5ef97d15c2eb10d154ae7ae52aee0531930ae4f5871a699abf4cefdcb1ff14cc70f3b6bf4bbfd619a6a4d8e8ab05c044
|
7
|
+
data.tar.gz: 5dc53ac8f096d39b85f1bfd39a47b1c6d1ed2f28d4d2a8bb0e14e9c0e687e18ab3960161fe774e47c21f0322a253b561dee46d826b554e058bbb8cdd0265dab1
|
data/.rubocop.yml
CHANGED
data/bin/manifold
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Manifold
|
4
|
+
module API
|
5
|
+
# Projects API
|
6
|
+
class Project
|
7
|
+
attr_reader :name, :logger, :directory
|
8
|
+
|
9
|
+
def initialize(name, logger: Logger.new($stdout), directory: Pathname.pwd.join(name))
|
10
|
+
@name = name
|
11
|
+
@logger = logger
|
12
|
+
@directory = Pathname(directory)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.create(name, directory: Pathname.pwd.join(name))
|
16
|
+
new(name, directory:).tap do |project|
|
17
|
+
[project.workspaces_directory, project.vectors_directory].each(&:mkpath)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def workspaces
|
22
|
+
@workspaces ||= workspace_directories.map { |dir| Workspace.from_directory(dir, logger:) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def generate
|
26
|
+
workspaces.each(&:generate)
|
27
|
+
end
|
28
|
+
|
29
|
+
def workspaces_directory
|
30
|
+
directory.join("workspaces")
|
31
|
+
end
|
32
|
+
|
33
|
+
def vectors_directory
|
34
|
+
directory.join("vectors")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def workspace_directories
|
40
|
+
workspaces_directory.children.select(&:directory?)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Manifold
|
4
|
+
module API
|
5
|
+
# Describes the entities for whom metrics are calculated.
|
6
|
+
class Vector
|
7
|
+
attr_reader :name, :template_path
|
8
|
+
|
9
|
+
DEFAULT_TEMPLATE_PATH = File.expand_path(
|
10
|
+
"../templates/vector_template.yml", __dir__
|
11
|
+
).freeze
|
12
|
+
|
13
|
+
def initialize(name, template_path: DEFAULT_TEMPLATE_PATH)
|
14
|
+
@name = name
|
15
|
+
@template_path = Pathname(template_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def add
|
19
|
+
directory.mkpath
|
20
|
+
FileUtils.cp(template_path, config_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def directory
|
26
|
+
Pathname.pwd.join("vectors")
|
27
|
+
end
|
28
|
+
|
29
|
+
def config_path
|
30
|
+
directory.join("#{name.downcase}.yml")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Manifold
|
4
|
+
module API
|
5
|
+
# Encapsulates a single manifold.
|
6
|
+
class Workspace
|
7
|
+
attr_reader :name, :template_path, :logger
|
8
|
+
|
9
|
+
DEFAULT_TEMPLATE_PATH = File.expand_path(
|
10
|
+
"../templates/workspace_template.yml", __dir__
|
11
|
+
).freeze
|
12
|
+
|
13
|
+
def initialize(name, template_path: DEFAULT_TEMPLATE_PATH, logger: Logger.new($stdout))
|
14
|
+
@name = name
|
15
|
+
@template_path = template_path
|
16
|
+
@logger = logger
|
17
|
+
@vector_service = Services::VectorService.new(logger)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_directory(directory, logger: Logger.new($stdout))
|
21
|
+
new(directory.basename.to_s, logger:)
|
22
|
+
end
|
23
|
+
|
24
|
+
def add
|
25
|
+
[tables_directory, routines_directory].each(&:mkpath)
|
26
|
+
FileUtils.cp(template_path, manifold_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate
|
30
|
+
return unless manifold_exists? && any_vectors?
|
31
|
+
|
32
|
+
generate_dimensions
|
33
|
+
logger.info("Generated BigQuery dimensions table schema for workspace '#{name}'.")
|
34
|
+
end
|
35
|
+
|
36
|
+
def tables_directory
|
37
|
+
directory.join("tables")
|
38
|
+
end
|
39
|
+
|
40
|
+
def routines_directory
|
41
|
+
directory.join("routines")
|
42
|
+
end
|
43
|
+
|
44
|
+
def manifold_file
|
45
|
+
return nil unless manifold_exists?
|
46
|
+
|
47
|
+
File.new(manifold_path)
|
48
|
+
end
|
49
|
+
|
50
|
+
def manifold_exists?
|
51
|
+
manifold_path.file?
|
52
|
+
end
|
53
|
+
|
54
|
+
def manifold_path
|
55
|
+
directory.join("manifold.yml")
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def directory
|
61
|
+
Pathname.pwd.join("workspaces", name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def manifold_yaml
|
65
|
+
@manifold_yaml ||= YAML.safe_load_file(manifold_path)
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_dimensions
|
69
|
+
dimensions_path.write(dimensions_schema_json.concat("\n"))
|
70
|
+
end
|
71
|
+
|
72
|
+
def dimensions_schema
|
73
|
+
[
|
74
|
+
{ "type" => "STRING", "name" => "id", "mode" => "REQUIRED" },
|
75
|
+
{ "type" => "RECORD", "name" => "dimensions", "mode" => "REQUIRED",
|
76
|
+
"fields" => dimensions_fields }
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
def dimensions_fields
|
81
|
+
vectors.filter_map do |vector|
|
82
|
+
logger.info("Loading vector schema for '#{vector}'.")
|
83
|
+
@vector_service.load_vector_schema(vector)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def dimensions_schema_json
|
88
|
+
JSON.pretty_generate(dimensions_schema)
|
89
|
+
end
|
90
|
+
|
91
|
+
def dimensions_path
|
92
|
+
tables_directory.join("dimensions.json")
|
93
|
+
end
|
94
|
+
|
95
|
+
def any_vectors?
|
96
|
+
!(vectors.nil? || vectors.empty?)
|
97
|
+
end
|
98
|
+
|
99
|
+
def vectors
|
100
|
+
manifold_yaml["vectors"]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/manifold/cli.rb
CHANGED
@@ -11,14 +11,12 @@ module Manifold
|
|
11
11
|
|
12
12
|
self.logger = logger
|
13
13
|
logger.level = Logger::INFO
|
14
|
-
|
15
|
-
self.bq_service = Services::BigQueryService.new(logger)
|
16
14
|
end
|
17
15
|
|
18
16
|
desc "init NAME", "Generate a new umbrella project for data management"
|
19
17
|
def init(name)
|
20
18
|
Manifold::API::Project.create(name)
|
21
|
-
logger.info "Created umbrella project '#{name}' with
|
19
|
+
logger.info "Created umbrella project '#{name}' with workspaces and vectors directories."
|
22
20
|
end
|
23
21
|
|
24
22
|
desc "vectors SUBCOMMAND ...ARGS", "Manage vectors"
|
@@ -33,28 +31,26 @@ module Manifold
|
|
33
31
|
end
|
34
32
|
|
35
33
|
desc "add VECTOR_NAME", "Add a new vector configuration"
|
36
|
-
def add(name
|
37
|
-
vector = API::Vector.new(name
|
34
|
+
def add(name)
|
35
|
+
vector = API::Vector.new(name)
|
38
36
|
vector.add
|
39
37
|
logger.info "Created vector configuration for '#{name}'."
|
40
38
|
end
|
41
39
|
}
|
42
40
|
|
43
41
|
desc "add WORKSPACE_NAME", "Add a new workspace to a project"
|
44
|
-
def add(name
|
45
|
-
workspace = API::Workspace.new(name
|
42
|
+
def add(name)
|
43
|
+
workspace = API::Workspace.new(name)
|
46
44
|
workspace.add
|
47
45
|
logger.info "Added workspace '#{name}' with tables and routines directories."
|
48
46
|
end
|
49
47
|
|
50
|
-
desc "generate
|
51
|
-
def generate
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
logger.error("Unsupported service: #{service}")
|
57
|
-
end
|
48
|
+
desc "generate", "Generate BigQuery schema for all workspaces in the project"
|
49
|
+
def generate
|
50
|
+
name = Pathname.pwd.basename.to_s
|
51
|
+
project = API::Project.new(name, directory: Pathname.pwd, logger:)
|
52
|
+
project.generate
|
53
|
+
logger.info "Generated BigQuery schema for all workspaces in the project."
|
58
54
|
end
|
59
55
|
end
|
60
56
|
end
|
@@ -10,14 +10,13 @@ module Manifold
|
|
10
10
|
|
11
11
|
def load_vector_schema(vector_name)
|
12
12
|
path = config_path(vector_name)
|
13
|
-
unless path.file?
|
14
|
-
@logger.error("Vector configuration not found: #{path}")
|
15
|
-
return nil
|
16
|
-
end
|
17
|
-
|
18
13
|
config = YAML.safe_load_file(path)
|
19
14
|
fields = transform_attributes_to_schema(config["attributes"])
|
20
15
|
{ "name" => vector_name.downcase, "type" => "RECORD", "fields" => fields }
|
16
|
+
rescue Errno::ENOENT, Errno::EISDIR
|
17
|
+
raise "Vector configuration not found: #{path}"
|
18
|
+
rescue Psych::Exception => e
|
19
|
+
raise "Invalid YAML in vector configuration #{path}: #{e.message}"
|
21
20
|
end
|
22
21
|
|
23
22
|
private
|
data/lib/manifold/version.rb
CHANGED
data/lib/manifold.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "json"
|
4
|
+
require "logger"
|
3
5
|
require "pathname"
|
4
6
|
require "thor"
|
5
7
|
require "yaml"
|
6
8
|
|
7
|
-
Dir[File.join(__dir__, "manifold", "**", "*.rb")].
|
9
|
+
Dir[File.join(__dir__, "manifold", "**", "*.rb")].each do |file|
|
8
10
|
require file
|
9
11
|
end
|
10
12
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: manifold-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- claytongentry
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -44,11 +44,10 @@ files:
|
|
44
44
|
- docs/CONTRIBUTING.md
|
45
45
|
- lib/manifold.rb
|
46
46
|
- lib/manifold/api.rb
|
47
|
+
- lib/manifold/api/project.rb
|
48
|
+
- lib/manifold/api/vector.rb
|
49
|
+
- lib/manifold/api/workspace.rb
|
47
50
|
- lib/manifold/cli.rb
|
48
|
-
- lib/manifold/project/project.rb
|
49
|
-
- lib/manifold/project/vector.rb
|
50
|
-
- lib/manifold/project/workspace.rb
|
51
|
-
- lib/manifold/services/big_query_service.rb
|
52
51
|
- lib/manifold/services/vector_service.rb
|
53
52
|
- lib/manifold/templates/vector_template.yml
|
54
53
|
- lib/manifold/templates/workspace_template.yml
|
@@ -61,6 +60,7 @@ metadata:
|
|
61
60
|
homepage_uri: https://github.com/bustle/manifold
|
62
61
|
source_code_uri: https://github.com/bustle/manifold
|
63
62
|
changelog_uri: https://github.com/bustle/manifold/CHANGELOG.md
|
63
|
+
rubygems_mfa_required: 'true'
|
64
64
|
post_install_message:
|
65
65
|
rdoc_options: []
|
66
66
|
require_paths:
|
@@ -69,7 +69,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
69
|
requirements:
|
70
70
|
- - ">="
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: 3.
|
72
|
+
version: 3.1.0
|
73
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
75
|
- - ">="
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Manifold
|
4
|
-
module API
|
5
|
-
# Projects API
|
6
|
-
class Project
|
7
|
-
attr_reader :name, :directory
|
8
|
-
|
9
|
-
def initialize(name, directory: Pathname.pwd.join(name))
|
10
|
-
self.name = name
|
11
|
-
self.directory = Pathname(directory)
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.create(name, directory: Pathname.pwd.join(name))
|
15
|
-
new(name, directory: directory).tap do |project|
|
16
|
-
[project.workspaces_directory, project.vectors_directory].each(&:mkpath)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def workspaces_directory
|
21
|
-
directory.join("workspaces")
|
22
|
-
end
|
23
|
-
|
24
|
-
def vectors_directory
|
25
|
-
directory.join("vectors")
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
attr_writer :name, :directory
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Manifold
|
4
|
-
module API
|
5
|
-
# Describes the entities for whom metrics are calculated.
|
6
|
-
class Vector
|
7
|
-
attr_reader :name, :project, :template_path
|
8
|
-
|
9
|
-
DEFAULT_TEMPLATE_PATH = Pathname.pwd.join(
|
10
|
-
"lib", "manifold", "templates", "vector_template.yml"
|
11
|
-
).freeze
|
12
|
-
|
13
|
-
def initialize(name, project:, template_path: DEFAULT_TEMPLATE_PATH)
|
14
|
-
self.name = name
|
15
|
-
self.project = project
|
16
|
-
self.template_path = Pathname(template_path)
|
17
|
-
end
|
18
|
-
|
19
|
-
def add
|
20
|
-
directory.mkpath
|
21
|
-
FileUtils.cp(template_path, config_path)
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
attr_writer :name, :project, :template_path
|
27
|
-
|
28
|
-
def directory
|
29
|
-
project.directory.join("vectors")
|
30
|
-
end
|
31
|
-
|
32
|
-
def config_path
|
33
|
-
directory.join("#{name.downcase}.yml")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Manifold
|
4
|
-
module API
|
5
|
-
# Encapsulates a single manifold.
|
6
|
-
class Workspace
|
7
|
-
attr_reader :name, :project, :template_path
|
8
|
-
|
9
|
-
DEFAULT_TEMPLATE_PATH = Pathname.pwd.join(
|
10
|
-
"lib", "manifold", "templates", "workspace_template.yml"
|
11
|
-
)
|
12
|
-
|
13
|
-
def initialize(name, project:, template_path: DEFAULT_TEMPLATE_PATH)
|
14
|
-
self.name = name
|
15
|
-
self.project = project
|
16
|
-
self.template_path = template_path
|
17
|
-
end
|
18
|
-
|
19
|
-
def add
|
20
|
-
[tables_directory, routines_directory].each(&:mkpath)
|
21
|
-
FileUtils.cp(template_path, manifold_path)
|
22
|
-
end
|
23
|
-
|
24
|
-
def tables_directory
|
25
|
-
project.workspaces_directory.join(name, "tables")
|
26
|
-
end
|
27
|
-
|
28
|
-
def routines_directory
|
29
|
-
project.workspaces_directory.join(name, "routines")
|
30
|
-
end
|
31
|
-
|
32
|
-
def manifold_file
|
33
|
-
return nil unless manifold_exists?
|
34
|
-
|
35
|
-
File.new(manifold_path)
|
36
|
-
end
|
37
|
-
|
38
|
-
def manifold_exists?
|
39
|
-
manifold_path.file?
|
40
|
-
end
|
41
|
-
|
42
|
-
def manifold_path
|
43
|
-
project.workspaces_directory.join(name, "manifold.yml")
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
attr_writer :name, :project, :template_path
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Manifold
|
4
|
-
module Services
|
5
|
-
# Handles the generation of BigQuery schemas based on project configurations
|
6
|
-
class BigQueryService
|
7
|
-
def initialize(logger)
|
8
|
-
@logger = logger
|
9
|
-
@vector_service = Manifold::Services::VectorService.new(logger)
|
10
|
-
end
|
11
|
-
|
12
|
-
def generate_dimensions_schema(project_name)
|
13
|
-
config_path = Pathname.pwd.join("projects", project_name, "manifold.yml")
|
14
|
-
return unless validate_config_exists(config_path, project_name)
|
15
|
-
|
16
|
-
config = YAML.safe_load_file(config_path)
|
17
|
-
|
18
|
-
fields = config["vectors"].reduce([]) do |list, vector|
|
19
|
-
@logger.info("Loading vector schema for '#{vector}'.")
|
20
|
-
[*@vector_service.load_vector_schema(vector), *list]
|
21
|
-
end
|
22
|
-
|
23
|
-
create_dimensions_file(project_name, fields)
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def validate_config_exists(config_path, project_name)
|
29
|
-
unless config_path.file?
|
30
|
-
@logger.error("Config file missing for project '#{project_name}'.")
|
31
|
-
return false
|
32
|
-
end
|
33
|
-
true
|
34
|
-
end
|
35
|
-
|
36
|
-
def create_dimensions_file(project_name, fields)
|
37
|
-
tables_directory(project_name).mkpath
|
38
|
-
dimensions = dimensions_schema(fields)
|
39
|
-
|
40
|
-
dimensions_path(project_name).write(dimensions)
|
41
|
-
@logger.info("Generated BigQuery dimensions table schema for '#{project_name}'.")
|
42
|
-
end
|
43
|
-
|
44
|
-
def dimensions_schema(fields)
|
45
|
-
JSON.pretty_generate([
|
46
|
-
{ "type" => "STRING", "name" => "id", "mode" => "REQUIRED" },
|
47
|
-
{ "type" => "RECORD", "name" => "dimensions", "mode" => "REQUIRED",
|
48
|
-
"fields" => fields }
|
49
|
-
]).concat("\n")
|
50
|
-
end
|
51
|
-
|
52
|
-
def tables_directory(project_name)
|
53
|
-
Pathname.pwd.join("projects", project_name, "bq", "tables")
|
54
|
-
end
|
55
|
-
|
56
|
-
def dimensions_path(project_name)
|
57
|
-
tables_directory(project_name).join("dimensions.json")
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|