manifold-cli 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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