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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f50907061a12bacfb9d34d83f56fcf6bc98317c6516a216340450fef78cebe3f
4
- data.tar.gz: b1722162e241d3b7275bd5675c457ce53c264d425885cc1bf35b9ae4bc62db99
3
+ metadata.gz: d0478f04fda1c307a346fb2e0a58f93949e73fadbf533b10d1bc374d3c9dd6b3
4
+ data.tar.gz: b6b8fcd1cdc6291af4a01b2014d61257f7b4ac585f54e1f93beaabfc31b5a511
5
5
  SHA512:
6
- metadata.gz: aa63a9e3a52d441d6f742efb8bab2037d67f2d12b11f8f22b835949ae3be26c3e7a01a2b15b288fcc9c9222fccd2ccb4ade3f091f01f52c678caded9fcf6733a
7
- data.tar.gz: df8807050b34f49d84741afcfad06a9c4d20406f3e67e8ad9a531fc523d47d120f630693660624ff1a2a6ba3c19d485bb7bc6340d7e70bd2aebe96f425821668
6
+ metadata.gz: fb8477bd304089eb1d48c5b2f276333b5ef97d15c2eb10d154ae7ae52aee0531930ae4f5871a699abf4cefdcb1ff14cc70f3b6bf4bbfd619a6a4d8e8ab05c044
7
+ data.tar.gz: 5dc53ac8f096d39b85f1bfd39a47b1c6d1ed2f28d4d2a8bb0e14e9c0e687e18ab3960161fe774e47c21f0322a253b561dee46d826b554e058bbb8cdd0265dab1
data/.rubocop.yml CHANGED
@@ -3,7 +3,8 @@ inherit_from: .rubocop_todo.yml
3
3
  require: rubocop-rspec
4
4
 
5
5
  AllCops:
6
- TargetRubyVersion: 3.0
6
+ NewCops: enable
7
+ TargetRubyVersion: 3.1
7
8
 
8
9
  Style/StringLiterals:
9
10
  EnforcedStyle: double_quotes
data/bin/manifold CHANGED
@@ -4,6 +4,6 @@
4
4
  lib_path = File.expand_path("../lib", Dir.pwd)
5
5
  $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
6
6
 
7
- require "manifold/cli"
7
+ require "manifold"
8
8
 
9
9
  Manifold::CLI.start(ARGV)
@@ -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 projects and vectors directories."
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, project: API::Project.new(File.basename(Dir.getwd)))
37
- vector = API::Vector.new(name, project: project)
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, project: API::Project.new(File.basename(Dir.getwd)))
45
- workspace = API::Workspace.new(name, project: project)
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 PROJECT_NAME SERVICE", "Generate services for a project"
51
- def generate(project_name, service)
52
- case service
53
- when "bq"
54
- bq_service.generate_dimensions_schema(project_name)
55
- else
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Manifold
4
- VERSION = "0.0.7"
4
+ VERSION = "0.0.8"
5
5
  end
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")].sort.each do |file|
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.7
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 00:00:00.000000000 Z
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.0.0
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