capsium 0.1.2 → 0.2.0

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +34 -7
  4. data/CHANGELOG.md +58 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE +21 -0
  7. data/README.adoc +177 -135
  8. data/Rakefile +8 -1
  9. data/bin/console +1 -1
  10. data/capsium.gemspec +11 -19
  11. data/exe/capsium +1 -1
  12. data/lib/capsium/cli/convert.rb +18 -0
  13. data/lib/capsium/cli/package.rb +86 -0
  14. data/lib/capsium/cli/reactor.rb +25 -0
  15. data/lib/capsium/cli.rb +3 -96
  16. data/lib/capsium/converters/jekyll.rb +5 -7
  17. data/lib/capsium/converters.rb +7 -0
  18. data/lib/capsium/package/dataset.rb +67 -34
  19. data/lib/capsium/package/manifest.rb +33 -22
  20. data/lib/capsium/package/manifest_config.rb +52 -7
  21. data/lib/capsium/package/metadata.rb +4 -8
  22. data/lib/capsium/package/metadata_config.rb +97 -9
  23. data/lib/capsium/package/routes.rb +39 -41
  24. data/lib/capsium/package/routes_config.rb +120 -44
  25. data/lib/capsium/package/security.rb +123 -0
  26. data/lib/capsium/package/security_config.rb +60 -0
  27. data/lib/capsium/package/storage.rb +20 -45
  28. data/lib/capsium/package/storage_config.rb +100 -3
  29. data/lib/capsium/package/validator.rb +149 -0
  30. data/lib/capsium/package.rb +68 -66
  31. data/lib/capsium/packager.rb +42 -6
  32. data/lib/capsium/reactor/introspection.rb +88 -0
  33. data/lib/capsium/reactor.rb +89 -41
  34. data/lib/capsium/version.rb +1 -1
  35. data/lib/capsium.rb +7 -9
  36. data/sig/capsium/package/dataset.rbs +28 -0
  37. data/sig/capsium/package/manifest.rbs +34 -0
  38. data/sig/capsium/package/manifest_config.rbs +38 -0
  39. data/sig/capsium/package/metadata.rbs +26 -0
  40. data/sig/capsium/package/metadata_config.rbs +49 -0
  41. data/sig/capsium/package/routes.rbs +37 -0
  42. data/sig/capsium/package/routes_config.rbs +74 -0
  43. data/sig/capsium/package/security.rbs +65 -0
  44. data/sig/capsium/package/security_config.rbs +57 -0
  45. data/sig/capsium/package/storage.rbs +28 -0
  46. data/sig/capsium/package/storage_config.rbs +61 -0
  47. data/sig/capsium/package/validator.rbs +29 -0
  48. data/sig/capsium/package.rbs +53 -0
  49. data/sig/capsium/packager.rbs +23 -0
  50. data/sig/capsium/reactor/introspection.rbs +31 -0
  51. data/sig/capsium/reactor.rbs +30 -0
  52. data/sig/capsium.rbs +3 -1
  53. metadata +52 -183
  54. data/lib/capsium/package/dataset_config.rb +0 -28
  55. data/lib/capsium/protector.rb +0 -103
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capsium
4
+ class Cli
5
+ class Convert < Thor
6
+ extend ThorExt::Start
7
+
8
+ desc "jekyll PACKAGE_FILE OUTPUT_DIRECTORY",
9
+ "Convert a Capsium package to a Jekyll site"
10
+
11
+ def jekyll(package_file, output_directory)
12
+ converter = Capsium::Converters::Jekyll.new(package_file,
13
+ output_directory)
14
+ converter.convert
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Capsium
6
+ class Cli
7
+ class Package < Thor
8
+ extend ThorExt::Start
9
+
10
+ desc "info PACKAGE_PATH", "Display information about the package"
11
+
12
+ def info(path_to_package)
13
+ package = Capsium::Package.new(path_to_package)
14
+ puts "Package Path: #{package.path}"
15
+ puts "Routes: #{package.routes.to_hash}"
16
+ puts "Manifest: #{package.manifest.to_hash}"
17
+ end
18
+
19
+ desc "manifest PATH_TO_PACKAGE",
20
+ "Show the manifest content of the package"
21
+
22
+ def manifest(path_to_package)
23
+ package = Capsium::Package.new(path_to_package)
24
+ puts JSON.pretty_generate(package.manifest.to_hash)
25
+ end
26
+
27
+ desc "storage PATH_TO_PACKAGE", "Show the storage datasets of the package"
28
+
29
+ def storage(path_to_package)
30
+ package = Capsium::Package.new(path_to_package)
31
+ puts JSON.pretty_generate(package.storage.to_hash)
32
+ end
33
+
34
+ desc "routes PATH_TO_PACKAGE", "Show the routes of the package"
35
+
36
+ def routes(path_to_package)
37
+ package = Capsium::Package.new(path_to_package)
38
+ puts JSON.pretty_generate(package.routes.to_hash)
39
+ end
40
+
41
+ desc "metadata PATH_TO_PACKAGE", "Show the metadata of the package"
42
+
43
+ def metadata(path_to_package)
44
+ package = Capsium::Package.new(path_to_package)
45
+ puts JSON.pretty_generate(package.metadata.to_hash)
46
+ end
47
+
48
+ desc "pack PATH_TO_PACKAGE_FOLDER", "Package the files into the package"
49
+ option :force, type: :boolean, default: false, aliases: "-f"
50
+
51
+ def pack(path_to_package)
52
+ package = Capsium::Package.new(path_to_package)
53
+ packager = Capsium::Packager.new
54
+ packager.pack(package, options)
55
+ end
56
+
57
+ desc "unpack PACKAGE_FILE", "Unpack a .cap package into a directory"
58
+ option :output, type: :string, aliases: "-o", desc: "Output directory"
59
+
60
+ def unpack(path_to_package)
61
+ destination = options[:output] || File.basename(path_to_package, ".cap")
62
+ Capsium::Packager.new.unpack(path_to_package, destination)
63
+ puts "Package unpacked to: #{destination}"
64
+ end
65
+
66
+ desc "validate PACKAGE_PATH", "Validate a package directory or .cap file"
67
+
68
+ def validate(path_to_package)
69
+ results = Capsium::Package::Validator.new(path_to_package).run
70
+ results.each { |result| puts format_result(result) }
71
+ return if results.all?(&:ok?)
72
+
73
+ raise Thor::Error, "Package validation failed"
74
+ end
75
+
76
+ private
77
+
78
+ def format_result(result)
79
+ line = "#{result.ok? ? 'PASS' : 'FAIL'} #{result.name}"
80
+ return line if result.messages.empty?
81
+
82
+ "#{line}: #{result.messages.join('; ')}"
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capsium
4
+ class Cli
5
+ class Reactor < Thor
6
+ extend ThorExt::Start
7
+
8
+ desc "serve PACKAGE_PATH",
9
+ "Start the Capsium reactor to serve the package"
10
+ option :port, type: :numeric, default: Capsium::Reactor::DEFAULT_PORT
11
+ option :do_not_listen, type: :boolean, default: false
12
+
13
+ def serve(path_to_package)
14
+ reactor = Capsium::Reactor.new(
15
+ package: path_to_package,
16
+ port: options[:port],
17
+ do_not_listen: options[:do_not_listen]
18
+ )
19
+ reactor.serve
20
+ ensure
21
+ reactor&.package&.cleanup
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/capsium/cli.rb CHANGED
@@ -1,107 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # lib/capsium/cli.rb
4
3
  require "thor"
5
- require_relative "package"
6
- require_relative "reactor"
7
- require_relative "thor_ext"
8
- require_relative "converters/jekyll"
9
4
 
10
5
  module Capsium
11
6
  class Cli < Thor
12
7
  extend ThorExt::Start
13
8
 
14
- class Package < Thor
15
- extend ThorExt::Start
16
-
17
- desc "info PACKAGE_PATH", "Display information about the package"
18
-
19
- def info(path_to_package)
20
- package = Capsium::Package.new(path_to_package)
21
- puts "Package Path: #{package.path}"
22
- puts "Routes: #{package.routes.as_json}"
23
- puts "Manifest: #{package.manifest.as_json}"
24
- end
25
-
26
- desc "manifest PATH_TO_PACKAGE",
27
- "Show the manifest content of the package"
28
-
29
- def manifest(path_to_package)
30
- package = Capsium::Package.new(path_to_package)
31
- puts JSON.pretty_generate(package.manifest.as_json)
32
- end
33
-
34
- desc "storage PATH_TO_PACKAGE", "Show the storage datasets of the package"
35
-
36
- def storage(path_to_package)
37
- package = Capsium::Package.new(path_to_package)
38
- puts JSON.pretty_generate(package.storage.as_json)
39
- end
40
-
41
- desc "routes PATH_TO_PACKAGE", "Show the routes of the package"
42
-
43
- def routes(path_to_package)
44
- package = Capsium::Package.new(path_to_package)
45
- puts JSON.pretty_generate(package.routes.as_json)
46
- end
47
-
48
- desc "metadata PATH_TO_PACKAGE", "Show the metadata of the package"
49
-
50
- def metadata(path_to_package)
51
- package = Capsium::Package.new(path_to_package)
52
- puts JSON.pretty_generate(package.metadata.as_json)
53
- end
54
-
55
- desc "pack PATH_TO_PACKAGE_FOLDER", "Package the files into the package"
56
- option :force, type: :boolean, default: false, aliases: "-f"
57
-
58
- def pack(path_to_package)
59
- package = Capsium::Package.new(path_to_package)
60
- packager = Capsium::Packager.new
61
- packager.pack(package, options)
62
- rescue StandardError => e
63
- puts e
64
- puts e.inspect
65
- puts e.backtrace
66
- end
67
- end
68
-
69
- class Reactor < Thor
70
- extend ThorExt::Start
71
-
72
- desc "serve PACKAGE_PATH",
73
- "Start the Capsium reactor to serve the package"
74
- option :port, type: :numeric, default: Capsium::Reactor::DEFAULT_PORT
75
- option :do_not_listen, type: :boolean, default: false
76
-
77
- def serve(path_to_package)
78
- reactor = Capsium::Reactor.new(
79
- package: path_to_package,
80
- port: options[:port],
81
- do_not_listen: options[:do_not_listen],
82
- )
83
- reactor.serve
84
- rescue StandardError => e
85
- puts e
86
- puts e.inspect
87
- puts e.backtrace
88
- ensure
89
- reactor.package.cleanup
90
- end
91
- end
92
-
93
- class Convert < Thor
94
- extend ThorExt::Start
95
-
96
- desc "jekyll PACKAGE_FILE OUTPUT_DIRECTORY",
97
- "Convert a Capsium package to a Jekyll site"
98
-
99
- def jekyll(package_file, output_directory)
100
- converter = Capsium::Converters::Jekyll.new(package_file,
101
- output_directory)
102
- converter.convert
103
- end
104
- end
9
+ autoload :Convert, "capsium/cli/convert"
10
+ autoload :Package, "capsium/cli/package"
11
+ autoload :Reactor, "capsium/cli/reactor"
105
12
 
106
13
  desc "package SUBCOMMAND ...ARGS", "Manage packages"
107
14
  subcommand "package", Capsium::Cli::Package
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # lib/capsium/converters/jekyll.rb
4
3
  require "fileutils"
5
- require "json"
6
- require "capsium/package"
4
+ require "pathname"
5
+ require "yaml"
7
6
 
8
7
  module Capsium
9
8
  module Converters
@@ -38,7 +37,7 @@ module Capsium
38
37
  "baseurl" => "",
39
38
  "url" => "",
40
39
  "markdown" => "kramdown",
41
- "theme" => "minima",
40
+ "theme" => "minima"
42
41
  }
43
42
  write_file("_config.yml", config.to_yaml)
44
43
  end
@@ -51,7 +50,7 @@ module Capsium
51
50
 
52
51
  def copy_content_files(package)
53
52
  package.content_files.each do |file_path|
54
- relative_path = Pathname.new(file_path).relative_path_from(Pathname.new(package.instance_variable_get(:@path)))
53
+ relative_path = Pathname.new(file_path).relative_path_from(Pathname.new(package.path))
55
54
  output_path = File.join(@output_directory, relative_path)
56
55
  FileUtils.mkdir_p(File.dirname(output_path))
57
56
  FileUtils.cp(file_path, output_path)
@@ -63,8 +62,7 @@ module Capsium
63
62
  route.path == "/"
64
63
  end
65
64
  if root_route
66
- index_path = File.join(package.instance_variable_get(:@path),
67
- root_route.target.file)
65
+ index_path = File.join(package.path, root_route.resource)
68
66
  index_content = File.read(index_path)
69
67
  write_file("index.html", index_content)
70
68
  else
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capsium
4
+ module Converters
5
+ autoload :Jekyll, "capsium/converters/jekyll"
6
+ end
7
+ end
@@ -1,72 +1,105 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
- require "yaml"
5
3
  require "csv"
6
- require "sqlite3"
7
- require "shale"
4
+ require "forwardable"
5
+ require "json"
8
6
  require "json-schema"
9
- require_relative "dataset_config"
7
+ require "sqlite3"
8
+ require "yaml"
10
9
 
11
10
  module Capsium
12
11
  class Package
12
+ # A loaded dataset. "source"/"schemaFile"/"databaseFile" are
13
+ # package-relative paths (ARCHITECTURE.md section 5), resolved against
14
+ # the package directory.
13
15
  class Dataset
14
- attr_reader :config, :data, :data_path
16
+ attr_reader :name, :config, :data, :package_path
15
17
 
16
18
  extend Forwardable
17
19
 
18
20
  def_delegators :@config, :to_json
19
21
 
20
- def initialize(config:, data_path: nil)
22
+ def initialize(name:, config:, package_path:)
23
+ @name = name
21
24
  @config = config
22
- @data_path = data_path || config.source
25
+ @package_path = package_path
23
26
  @data = load_data
24
27
  end
25
28
 
29
+ def source_path
30
+ File.join(@package_path, @config.backing_file)
31
+ end
32
+
33
+ def schema_path
34
+ return unless @config.schema_file
35
+
36
+ File.join(@package_path, @config.schema_file)
37
+ end
38
+
26
39
  def load_data
40
+ return nil unless File.file?(source_path)
41
+
27
42
  case @config.format
28
- when "yaml" then YAML.load_file(@data_path)
29
- when "json" then JSON.parse(File.read(@data_path))
30
- when "csv" then CSV.read(@data_path, headers: true)
31
- when "tsv" then CSV.read(@data_path, col_sep: "\t", headers: true)
43
+ when "yaml" then YAML.load_file(source_path)
44
+ when "json" then JSON.parse(File.read(source_path))
45
+ when "csv" then CSV.read(source_path, headers: true)
46
+ when "tsv" then CSV.read(source_path, col_sep: "\t", headers: true)
32
47
  when "sqlite" then load_sqlite_data
33
48
  else
34
- raise "Unsupported data file type: #{@config.format}"
49
+ raise Error, "Unsupported data file type: #{@config.format}"
35
50
  end
36
51
  end
37
52
 
38
53
  def validate
39
- return unless @config.schema
54
+ return true unless @config.schema_file && @config.schema_type == "json-schema"
40
55
 
41
- schema_path = File.join(File.dirname(@data_path), @config.schema)
42
- schema = YAML.load_file(schema_path) if @config.format == "yaml"
43
- schema = JSON.parse(File.read(schema_path)) if @config.format == "json"
56
+ JSON::Validator.validate!(load_schema, @data.to_json)
57
+ end
44
58
 
45
- case @config.format
46
- when "yaml" then YAML.load_file(@data_path)
47
- when "json" then JSON.parse(File.read(@data_path))
48
- else
49
- raise "Validation is only supported for YAML and JSON formats"
59
+ # File-existence and schema validations for this dataset. Returns a
60
+ # list of human-readable problems; empty when valid.
61
+ def validation_errors
62
+ problems = []
63
+ unless File.file?(source_path)
64
+ problems << "dataset source missing on disk: #{@config.backing_file}"
50
65
  end
51
-
52
- JSON::Validator.validate!(schema, @data.to_json)
66
+ problems.concat(schema_validation_errors)
67
+ problems
53
68
  end
54
69
 
55
- def save_to_file(output_path)
56
- File.write(output_path, to_json)
70
+ private
71
+
72
+ def schema_validation_errors
73
+ return [] unless @config.schema_file
74
+ unless File.file?(schema_path)
75
+ return ["dataset schema missing on disk: #{@config.schema_file}"]
76
+ end
77
+
78
+ validate
79
+ []
80
+ rescue JSON::Schema::ValidationError => e
81
+ ["dataset #{@name} fails schema validation: #{e.message}"]
57
82
  end
58
83
 
59
- private
84
+ def load_schema
85
+ case File.extname(schema_path).downcase
86
+ when ".yaml", ".yml" then YAML.load_file(schema_path)
87
+ else JSON.parse(File.read(schema_path))
88
+ end
89
+ end
60
90
 
61
91
  def load_sqlite_data
62
- db = SQLite3::Database.new(@data_path)
63
- tables = db.execute("SELECT name FROM sqlite_master WHERE type='table';")
64
- data = {}
65
- tables.each do |table|
66
- table_name = table.first
67
- data[table_name] = db.execute("SELECT * FROM #{table_name};")
92
+ db = SQLite3::Database.new(source_path)
93
+ tables = @config.table ? [@config.table] : sqlite_tables(db)
94
+ tables.to_h do |table_name|
95
+ [table_name, db.execute("SELECT * FROM #{table_name};")]
68
96
  end
69
- data
97
+ ensure
98
+ db&.close
99
+ end
100
+
101
+ def sqlite_tables(db)
102
+ db.execute("SELECT name FROM sqlite_master WHERE type='table';").map(&:first)
70
103
  end
71
104
  end
72
105
  end
@@ -1,51 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # lib/capsium/package/manifest.rb
4
- require "json"
3
+ require "forwardable"
5
4
  require "marcel"
6
- require "shale"
7
- require_relative "manifest_config"
5
+ require "pathname"
8
6
 
9
7
  module Capsium
10
8
  class Package
11
9
  class Manifest
12
10
  extend Forwardable
13
- attr_accessor :path, :content_path, :config
14
11
 
15
- def_delegators :@config, :to_json
12
+ attr_reader :path, :content_path, :config
13
+
14
+ def_delegators :@config, :to_hash
16
15
 
17
16
  def initialize(path)
18
17
  @path = path
19
18
  @content_path = File.join(File.dirname(@path), Package::CONTENT_DIR)
20
-
21
19
  @config = if File.exist?(path)
22
20
  ManifestConfig.from_json(File.read(path))
23
21
  else
24
- ManifestConfig.new(content: generate_manifest)
22
+ ManifestConfig.new(resources: generate_manifest)
25
23
  end
26
24
  end
27
25
 
26
+ # Auto-generation (ARCHITECTURE.md section 3): scan content/
27
+ # recursively, detect MIME types, default visibility "exported",
28
+ # deterministic (sorted) output.
28
29
  def generate_manifest
29
- files = Dir[File.join(@content_path, "**", "*")].reject do |f|
30
- File.directory?(f)
30
+ content_files.sort.to_h do |file_path|
31
+ [relative_path(file_path),
32
+ Resource.new(type: mime_from_path(file_path), visibility: "exported")]
31
33
  end
34
+ end
32
35
 
33
- files.sort.map do |file_path|
34
- ManifestConfigItem.new(
35
- file: relative_path(file_path),
36
- mime: mime_from_path(file_path),
37
- )
38
- end
36
+ def resources
37
+ @config.resources
39
38
  end
40
39
 
41
- def lookup(filename)
42
- @config.content.detect do |data_item|
43
- data_item.file == filename
44
- end
40
+ def lookup(path)
41
+ resources[path]
42
+ end
43
+
44
+ def type_for(path)
45
+ resource = lookup(path)
46
+ resource&.type
47
+ end
48
+
49
+ def to_json(*_args)
50
+ @config.to_json
45
51
  end
46
52
 
47
53
  def save_to_file(output_path = @path)
48
- @config.content.sort_by!(&:file)
49
54
  File.write(output_path, to_json)
50
55
  end
51
56
 
@@ -65,11 +70,17 @@ module Capsium
65
70
 
66
71
  private
67
72
 
73
+ def content_files
74
+ Dir.glob(File.join(@content_path, "**", "*")).select do |file|
75
+ File.file?(file)
76
+ end
77
+ end
78
+
68
79
  def mime_from_path(path)
69
80
  Marcel::MimeType.for(
70
81
  Pathname.new(path),
71
82
  name: File.basename(path),
72
- extension: File.extname(path),
83
+ extension: File.extname(path)
73
84
  )
74
85
  end
75
86
  end
@@ -1,17 +1,62 @@
1
- require "shale"
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "lutaml/model"
2
5
 
3
6
  module Capsium
4
7
  class Package
5
- class ManifestConfigItem < Shale::Mapper
6
- attribute :file, Shale::Type::String
7
- attribute :mime, Shale::Type::String
8
+ # A single manifest resource entry (ARCHITECTURE.md section 3).
9
+ class Resource < Lutaml::Model::Serializable
10
+ VISIBILITIES = %w[exported private].freeze
11
+
12
+ attribute :type, :string
13
+ attribute :visibility, :string, values: VISIBILITIES, default: "exported"
14
+ attribute :version, :string
15
+
16
+ json do
17
+ map :type, to: :type
18
+ map :visibility, to: :visibility
19
+ map :version, to: :version
20
+ end
8
21
  end
9
22
 
10
- class ManifestConfig < Shale::Mapper
11
- attribute :content, ManifestConfigItem, collection: true
23
+ # Canonical manifest.json model: an object keyed by package-relative
24
+ # resource path. The legacy gem form ({"content": [{file, mime}]}) is
25
+ # accepted on read and normalized; writers emit only the object form.
26
+ class ManifestConfig < Lutaml::Model::Serializable
27
+ attribute :resources, :hash, default: {}
28
+
29
+ json do
30
+ map :resources, with: { from: :resources_from_json, to: :resources_to_json }
31
+ end
32
+
33
+ def self.from_json(json)
34
+ doc = JSON.parse(json)
35
+ doc["resources"] ||= legacy_resources(doc.delete("content")) if doc.key?("content")
36
+ super(JSON.generate(doc))
37
+ end
38
+
39
+ def self.legacy_resources(content)
40
+ (content || []).to_h do |item|
41
+ [item["file"], { "type" => item["mime"], "visibility" => "exported" }]
42
+ end
43
+ end
44
+ private_class_method :legacy_resources
45
+
46
+ def resources_from_json(model, value)
47
+ model.resources = (value || {}).to_h do |path, attributes|
48
+ [path, Resource.from_json(JSON.generate(attributes))]
49
+ end
50
+ end
51
+
52
+ def resources_to_json(model, doc)
53
+ doc["resources"] = model.resources.sort.to_h do |path, resource|
54
+ [path, JSON.parse(resource.to_json)]
55
+ end
56
+ end
12
57
 
13
58
  def sort!
14
- @content.sort_by!(&:file)
59
+ self.resources = resources.sort.to_h
15
60
  self
16
61
  end
17
62
  end
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # lib/capsium/package/metadata.rb
4
- require "shale"
5
3
  require "forwardable"
6
- require_relative "metadata_config"
7
4
 
8
5
  module Capsium
9
6
  class Package
@@ -11,11 +8,10 @@ module Capsium
11
8
  attr_reader :path, :config
12
9
 
13
10
  extend Forwardable
14
- def_delegator :@config, :to_json
15
- def_delegator :@config, :name
16
- def_delegator :@config, :version
17
- def_delegator :@config, :description # Delegate description method
18
- def_delegator :@config, :dependencies
11
+
12
+ def_delegators :@config, :to_json, :to_hash, :name, :version,
13
+ :description, :guid, :uuid, :author, :license,
14
+ :repository, :dependencies
19
15
 
20
16
  def initialize(path)
21
17
  @path = path