capsium 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/rake.yml +18 -0
- data/.github/workflows/release.yml +24 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/README.adoc +258 -0
- data/Rakefile +12 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/capsium.gemspec +54 -0
- data/exe/capsium +6 -0
- data/images/005df7415a331c466ad2d9a42efdd212b6f42fe50b5fd3b3174c86c706f58244.png +0 -0
- data/images/0374025b3af99b8a473282c8cbbf9fcd29573cf41e586982f328f86c0690f43d.png +0 -0
- data/images/0bb4da785be40ef58e219470ebccb979325928b75453dc46bac23c6ee8a7a7cb.png +0 -0
- data/images/6aa294dccc81af594aacbe804e7ddffdc17eacc28357338108aea5d021d831ff.png +0 -0
- data/images/72dd3fbf3f4b475e27a0e7fb8137c475c32c41f8d222bcf62d6a9ccf102d9532.png +0 -0
- data/images/8772b6961d169738d7b0fa0b669b06fc2f40632d4c62586c7634fc17b93182a3.png +0 -0
- data/images/a998d842405933d45723606ff3f70162ec95b4ef30db25464a366184fd08fb9b.png +0 -0
- data/images/aa8980547e8c003d33273ab4d80e62da7f317bd7581b293c06d67f5331f24f31.png +0 -0
- data/images/bb78a872b539e0e9b2d80dee58acbb688f3f2727b324a5bf8bf417a69d94a166.png +0 -0
- data/images/c48fc83b17725d85fbb64d971196ebfccd8c5c757fe6aa5845303f6e315879b6.png +0 -0
- data/images/f08ef07308d08119ac2124bb7428c8bef17ef1ca70045696604d6e83015a9b91.png +0 -0
- data/images/f7514206111b695647eae9adfcf498ba3e0ff83ecfe25f3fc3ed8e9f04c5c726-1.png +0 -0
- data/images/f7514206111b695647eae9adfcf498ba3e0ff83ecfe25f3fc3ed8e9f04c5c726.png +0 -0
- data/lib/capsium/cli.rb +96 -0
- data/lib/capsium/converters/jekyll.rb +59 -0
- data/lib/capsium/package/dataset.rb +86 -0
- data/lib/capsium/package/manifest.rb +97 -0
- data/lib/capsium/package/metadata.rb +48 -0
- data/lib/capsium/package/routes.rb +157 -0
- data/lib/capsium/package/storage.rb +52 -0
- data/lib/capsium/package.rb +142 -0
- data/lib/capsium/packager.rb +100 -0
- data/lib/capsium/protector.rb +95 -0
- data/lib/capsium/reactor.rb +82 -0
- data/lib/capsium/thor_ext.rb +71 -0
- data/lib/capsium/version.rb +5 -0
- data/lib/capsium.rb +15 -0
- data/sig/capsium.rbs +4 -0
- metadata +354 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/capsium/converters/jekyll_to_capsium.rb
|
4
|
+
require "fileutils"
|
5
|
+
require "json"
|
6
|
+
require "capsium/packager"
|
7
|
+
|
8
|
+
module Capsium
|
9
|
+
module Converters
|
10
|
+
class Jekyll
|
11
|
+
def initialize(site_directory, output_directory)
|
12
|
+
@site_directory = site_directory
|
13
|
+
@output_directory = output_directory
|
14
|
+
end
|
15
|
+
|
16
|
+
def convert
|
17
|
+
validate_site_directory
|
18
|
+
package_directory = prepare_package_directory
|
19
|
+
packager = Capsium::Packager.new(package_directory)
|
20
|
+
packager.pack
|
21
|
+
cleanup_package_directory(package_directory)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def validate_site_directory
|
27
|
+
return if Dir.exist?(@site_directory) && File.exist?(File.join(@site_directory, "index.html"))
|
28
|
+
|
29
|
+
raise "Invalid Jekyll site directory: #{@site_directory}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def prepare_package_directory
|
33
|
+
package_directory = File.join(@output_directory, "capsium_package")
|
34
|
+
FileUtils.mkdir_p(package_directory)
|
35
|
+
|
36
|
+
FileUtils.cp_r(Dir.glob("#{@site_directory}/*"), package_directory)
|
37
|
+
|
38
|
+
create_manifest(package_directory)
|
39
|
+
|
40
|
+
package_directory
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_manifest(package_directory)
|
44
|
+
manifest = {
|
45
|
+
"name" => "jekyll_site",
|
46
|
+
"version" => "1.0.0",
|
47
|
+
"description" => "A Jekyll site converted to a Capsium package",
|
48
|
+
"files" => Dir.glob("#{package_directory}/**/*").map { |file| file.sub("#{package_directory}/", "") }
|
49
|
+
}
|
50
|
+
|
51
|
+
File.write(File.join(package_directory, "manifest.json"), JSON.pretty_generate(manifest))
|
52
|
+
end
|
53
|
+
|
54
|
+
def cleanup_package_directory(package_directory)
|
55
|
+
FileUtils.rm_rf(package_directory)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/capsium/package/dataset.rb
|
4
|
+
require "json"
|
5
|
+
require "yaml"
|
6
|
+
require "csv"
|
7
|
+
require "sqlite3"
|
8
|
+
|
9
|
+
module Capsium
|
10
|
+
class Package
|
11
|
+
class Dataset < Shale::Mapper
|
12
|
+
# {
|
13
|
+
# "datasets": [
|
14
|
+
# {
|
15
|
+
# "name": "animals",
|
16
|
+
# "source": "data/animals.yaml",
|
17
|
+
# "format": "yaml",
|
18
|
+
# "schema": "data/animals_schema.yaml"
|
19
|
+
# }
|
20
|
+
# ]
|
21
|
+
# }
|
22
|
+
attr_reader :name, :path, :type, :data
|
23
|
+
|
24
|
+
def initialize(path, data_path)
|
25
|
+
@path = path
|
26
|
+
@name = File.basename(@path, ".*")
|
27
|
+
@type = detect_type
|
28
|
+
@data_path = data_path
|
29
|
+
@data = load_data
|
30
|
+
end
|
31
|
+
|
32
|
+
def detect_type
|
33
|
+
case File.extname(@path).downcase
|
34
|
+
when /.ya?ml/ then :yaml
|
35
|
+
when ".json" then :json
|
36
|
+
when ".csv" then :csv
|
37
|
+
when ".tsv" then :tsv
|
38
|
+
when ".sqlite", ".db" then :sqlite
|
39
|
+
else
|
40
|
+
raise "Unsupported data file type: #{File.extname(@path)}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_data
|
45
|
+
case @type
|
46
|
+
when :yaml then YAML.load_file(@path)
|
47
|
+
when :json then JSON.parse(File.read(@path))
|
48
|
+
when :csv then CSV.read(@path, headers: true)
|
49
|
+
when :tsv then CSV.read(@path, col_sep: "\t", headers: true)
|
50
|
+
when :sqlite then load_sqlite_data
|
51
|
+
else
|
52
|
+
raise "Unsupported data file type: #{@type}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def as_json
|
57
|
+
{
|
58
|
+
name: name,
|
59
|
+
source: relative_path(path),
|
60
|
+
format: type.to_s
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_json(*_args)
|
65
|
+
JSON.pretty_generate(as_json)
|
66
|
+
end
|
67
|
+
|
68
|
+
def relative_path(path)
|
69
|
+
Pathname.new(path).relative_path_from(@data_path).to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def load_sqlite_data
|
75
|
+
db = SQLite3::Database.new(@path)
|
76
|
+
tables = db.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
77
|
+
data = {}
|
78
|
+
tables.each do |table|
|
79
|
+
table_name = table.first
|
80
|
+
data[table_name] = db.execute("SELECT * FROM #{table_name};")
|
81
|
+
end
|
82
|
+
data
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/capsium/package/manifest.rb
|
4
|
+
require "json"
|
5
|
+
require "marcel"
|
6
|
+
require "shale"
|
7
|
+
|
8
|
+
module Capsium
|
9
|
+
class Package
|
10
|
+
class Manifest
|
11
|
+
attr_accessor :path, :content_path, :data
|
12
|
+
|
13
|
+
def initialize(path)
|
14
|
+
@path = path
|
15
|
+
# This is {package-name}/content
|
16
|
+
# or
|
17
|
+
# /tmp/../content
|
18
|
+
@content_path = File.join(File.dirname(@path), "content")
|
19
|
+
|
20
|
+
@data = if File.exist?(path)
|
21
|
+
ManifestData.from_json(File.read(path))
|
22
|
+
else
|
23
|
+
ManifestData.new(content: generate_manifest)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_manifest
|
28
|
+
files = Dir[File.join(@content_path, "**", "*")].reject do |f|
|
29
|
+
File.directory?(f)
|
30
|
+
end
|
31
|
+
|
32
|
+
files.sort.map do |file_path|
|
33
|
+
ManifestDataItem.new(
|
34
|
+
file: relative_path(file_path),
|
35
|
+
mime: mime_from_path(file_path)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def lookup(filename)
|
41
|
+
@data.content.detect do |data_item|
|
42
|
+
data_item.file == filename
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_json(*_args)
|
47
|
+
@data.to_json
|
48
|
+
end
|
49
|
+
|
50
|
+
def save_to_file(output_path = @path)
|
51
|
+
File.open(output_path, "w") do |file|
|
52
|
+
file.write(to_json)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def path_to_content_file(path)
|
57
|
+
Pathname.new(@content_path).join(path)
|
58
|
+
end
|
59
|
+
|
60
|
+
def content_file_exists?(path)
|
61
|
+
File.exist?(path_to_content_file(path))
|
62
|
+
end
|
63
|
+
|
64
|
+
def relative_path(path)
|
65
|
+
Pathname.new(path).relative_path_from(@content_path).to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def mime_from_path(path)
|
71
|
+
Marcel::MimeType.for(
|
72
|
+
Pathname.new(path),
|
73
|
+
name: File.basename(path),
|
74
|
+
extension: File.extname(path)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ManifestDataItem < Shale::Mapper
|
80
|
+
attribute :file, Shale::Type::String
|
81
|
+
attribute :mime, Shale::Type::String
|
82
|
+
|
83
|
+
# json do
|
84
|
+
# map "file", to: :file
|
85
|
+
# map "mime", to: :mime
|
86
|
+
# end
|
87
|
+
end
|
88
|
+
|
89
|
+
class ManifestData < Shale::Mapper
|
90
|
+
attribute :content, ManifestDataItem, collection: true
|
91
|
+
|
92
|
+
# json do
|
93
|
+
# map "content", to: :content
|
94
|
+
# end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/capsium/package/metadata.rb
|
4
|
+
require "shale"
|
5
|
+
require "forwardable"
|
6
|
+
|
7
|
+
module Capsium
|
8
|
+
class Package
|
9
|
+
class Dependency < Shale::Mapper
|
10
|
+
attribute :name, Shale::Type::String
|
11
|
+
attribute :version, Shale::Type::String
|
12
|
+
end
|
13
|
+
|
14
|
+
class MetadataData < Shale::Mapper
|
15
|
+
attribute :name, Shale::Type::String
|
16
|
+
attribute :version, Shale::Type::String
|
17
|
+
attribute :dependencies, Dependency, collection: true
|
18
|
+
end
|
19
|
+
|
20
|
+
class Metadata
|
21
|
+
attr_reader :path, :data
|
22
|
+
|
23
|
+
extend Forwardable
|
24
|
+
def_delegator :@data, :name
|
25
|
+
def_delegator :@data, :version
|
26
|
+
def_delegator :@data, :dependencies
|
27
|
+
|
28
|
+
def initialize(path)
|
29
|
+
@path = path
|
30
|
+
@data = if File.exist?(path)
|
31
|
+
MetadataData.from_json(File.read(path))
|
32
|
+
else
|
33
|
+
MetadataData.new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_json(*_args)
|
38
|
+
@data.to_json
|
39
|
+
end
|
40
|
+
|
41
|
+
def save_to_file(output_path = @path)
|
42
|
+
File.open(output_path, "w") do |file|
|
43
|
+
file.write(to_json)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/capsium/package/routes.rb
|
4
|
+
require "json"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
module Capsium
|
8
|
+
class Package
|
9
|
+
class RouteTarget < Shale::Mapper
|
10
|
+
attribute :file, Shale::Type::String
|
11
|
+
|
12
|
+
def fs_path(manifest)
|
13
|
+
manifest.path_to_content_file(manifest.lookup(file).file)
|
14
|
+
end
|
15
|
+
|
16
|
+
def mime(manifest)
|
17
|
+
manifest.lookup(file).mime
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate(manifest)
|
21
|
+
target_path = fs_path(manifest)
|
22
|
+
return if File.exist?(target_path)
|
23
|
+
|
24
|
+
raise "Route target does not exist: #{target_path}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Route < Shale::Mapper
|
29
|
+
attribute :path, Shale::Type::String
|
30
|
+
attribute :target, RouteTarget
|
31
|
+
end
|
32
|
+
|
33
|
+
class RoutesData < Shale::Mapper
|
34
|
+
attribute :routes, Route, collection: true
|
35
|
+
|
36
|
+
def resolve(route)
|
37
|
+
routes.detect do |r|
|
38
|
+
r.path == route
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add(route, target)
|
43
|
+
target = RouteTarget.new(file: target) if target.is_a?(String)
|
44
|
+
|
45
|
+
@routes << Route.new(path: route, target: target)
|
46
|
+
end
|
47
|
+
|
48
|
+
def update(route, updated_route, _updated_target)
|
49
|
+
r = @routes.resolve(route)
|
50
|
+
r.path = updated_route
|
51
|
+
r.target = target
|
52
|
+
r
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove(route)
|
56
|
+
r = @routes.resolve(route)
|
57
|
+
@routes.remove(r)
|
58
|
+
end
|
59
|
+
|
60
|
+
def sort!
|
61
|
+
@routes.sort_by!(&:path)
|
62
|
+
self
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Routes
|
67
|
+
attr_reader :path, :data, :index, :manifest
|
68
|
+
|
69
|
+
ROUTES_FILE = "routes.json"
|
70
|
+
DEFAULT_INDEX_TARGET = "index.html"
|
71
|
+
INDEX_ROUTE = "/"
|
72
|
+
|
73
|
+
def initialize(path, manifest)
|
74
|
+
@path = path
|
75
|
+
@dir = File.dirname(path)
|
76
|
+
@manifest = manifest
|
77
|
+
@data = if File.exist?(path)
|
78
|
+
RoutesData.from_json(File.read(path))
|
79
|
+
else
|
80
|
+
generate_routes_from_manifest
|
81
|
+
end
|
82
|
+
validate_index_path(@data.resolve(INDEX_ROUTE).target.file)
|
83
|
+
validate
|
84
|
+
end
|
85
|
+
|
86
|
+
def resolve(url_path)
|
87
|
+
@data.resolve(url_path)
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_route(route, target)
|
91
|
+
validate_route_target(route, target)
|
92
|
+
@data.add(route, target)
|
93
|
+
end
|
94
|
+
|
95
|
+
def update_route(route, updated_route, updated_target)
|
96
|
+
validate_route_target(updated_route, updated_target)
|
97
|
+
@data.update(route, updated_route, updated_target)
|
98
|
+
end
|
99
|
+
|
100
|
+
def remove_route(route)
|
101
|
+
@data._removed(route)
|
102
|
+
end
|
103
|
+
|
104
|
+
def validate
|
105
|
+
@data.routes.each do |route|
|
106
|
+
route.target.validate(@manifest)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_json(*_args)
|
111
|
+
@data.sort!.to_json
|
112
|
+
end
|
113
|
+
|
114
|
+
def save_to_file(output_path = @path)
|
115
|
+
File.open(output_path, "w") do |file|
|
116
|
+
file.write(to_json)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def generate_routes_from_manifest
|
123
|
+
routes = RoutesData.new
|
124
|
+
@manifest.data.content.each do |data_item|
|
125
|
+
file_path = data_item.file
|
126
|
+
# mime_type = data_item.mime
|
127
|
+
content_path = @manifest.path_to_content_file(file_path).to_s
|
128
|
+
|
129
|
+
if file_path == DEFAULT_INDEX_TARGET
|
130
|
+
routes.add(INDEX_ROUTE, DEFAULT_INDEX_TARGET)
|
131
|
+
end
|
132
|
+
|
133
|
+
routes.add("/#{clean_target_html_path(file_path)}", file_path) if file_path =~ /\.html$/
|
134
|
+
|
135
|
+
routes.add("/#{file_path}", file_path)
|
136
|
+
end
|
137
|
+
|
138
|
+
routes
|
139
|
+
end
|
140
|
+
|
141
|
+
def clean_target_html_path(path)
|
142
|
+
File.dirname(path) + File.basename(path, ".html")
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def validate_index_path(index_path)
|
147
|
+
target_path = @manifest.path_to_content_file(index_path)
|
148
|
+
|
149
|
+
raise "Index file does not exist: #{target_path}" unless File.exist?(target_path)
|
150
|
+
|
151
|
+
return if File.extname(target_path).downcase == ".html"
|
152
|
+
|
153
|
+
raise "Index file is not an HTML file: #{target_path}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/capsium/package/storage.rb
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Capsium
|
7
|
+
class Package
|
8
|
+
class Storage
|
9
|
+
attr_reader :datasets
|
10
|
+
|
11
|
+
DATA_DIR = "data"
|
12
|
+
|
13
|
+
def initialize(path)
|
14
|
+
@path = path
|
15
|
+
@dir = File.dirname(path)
|
16
|
+
@datasets_path = File.join(@dir, DATA_DIR)
|
17
|
+
@datasets = load_datasets || generate_datasets
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_datasets
|
21
|
+
return unless File.exist?(@path)
|
22
|
+
|
23
|
+
storage_data = JSON.parse(File.read(@path))
|
24
|
+
@datasets = storage_data["storage"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def as_json
|
28
|
+
{ datasets: datasets.map(&:as_json) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_json(*_args)
|
32
|
+
JSON.pretty_generate(as_json)
|
33
|
+
end
|
34
|
+
|
35
|
+
def save_to_file(output_path = @path)
|
36
|
+
File.open(output_path, "w") do |file|
|
37
|
+
file.write(to_json)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_datasets
|
42
|
+
datasets = []
|
43
|
+
paths = File.join(@datasets_path, "*.{yaml,yml,json,csv,tsv,sqlite,db}")
|
44
|
+
Dir.glob(paths).each do |file_path|
|
45
|
+
datasets << Dataset.new(file_path, @datasets_path)
|
46
|
+
# dataset_info[:table] = dataset.table_name if dataset.type == :sqlite
|
47
|
+
end
|
48
|
+
datasets
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/capsium/package.rb
|
4
|
+
require "fileutils"
|
5
|
+
require "json"
|
6
|
+
require "yaml"
|
7
|
+
require "csv"
|
8
|
+
require "sqlite3"
|
9
|
+
require "zip"
|
10
|
+
require_relative "package/manifest"
|
11
|
+
require_relative "package/metadata"
|
12
|
+
require_relative "package/routes"
|
13
|
+
require_relative "package/dataset"
|
14
|
+
require_relative "package/storage"
|
15
|
+
require_relative "packager"
|
16
|
+
|
17
|
+
module Capsium
|
18
|
+
class Package
|
19
|
+
attr_reader :name, :path, :manifest, :metadata, :routes, :datasets, :storage
|
20
|
+
|
21
|
+
MANIFEST_FILE = "manifest.json"
|
22
|
+
METADATA_FILE = "metadata.json"
|
23
|
+
PACKAGING_FILE = "packaging.json"
|
24
|
+
SIGNATURE_FILE = "signature.json"
|
25
|
+
STORAGE_FILE = "storage.json"
|
26
|
+
ROUTES_FILE = "routes.json"
|
27
|
+
CONTENT_DIR = "content"
|
28
|
+
DATA_DIR = "data"
|
29
|
+
ENCRYPTED_PACKAGING_FILE = "package.enc"
|
30
|
+
|
31
|
+
def initialize(path)
|
32
|
+
@original_path = Pathname.new(path).expand_path
|
33
|
+
@path = prepare_package(@original_path)
|
34
|
+
create_package_structure
|
35
|
+
load_package
|
36
|
+
@name = metadata.name
|
37
|
+
end
|
38
|
+
|
39
|
+
def prepare_package(path)
|
40
|
+
return path if File.directory?(path)
|
41
|
+
|
42
|
+
if File.file?(path)
|
43
|
+
return decompress_cap_file(path) if File.extname(path) == ".cap"
|
44
|
+
|
45
|
+
raise "Error: The package must have a .cap extension"
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
raise "Invalid package path: #{path}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def solidify
|
53
|
+
@manifest.save_to_file
|
54
|
+
@metadata.save_to_file
|
55
|
+
@routes.save_to_file
|
56
|
+
@storage.save_to_file
|
57
|
+
end
|
58
|
+
|
59
|
+
def decompress_cap_file(file_path)
|
60
|
+
temp_dir = Dir.mktmpdir
|
61
|
+
metadata_path = File.join(temp_dir, METADATA_FILE)
|
62
|
+
|
63
|
+
# Extract metadata.json first
|
64
|
+
Zip::File.open(file_path) do |zip_file|
|
65
|
+
if entry = zip_file.find_entry(METADATA_FILE)
|
66
|
+
entry.extract(metadata_path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
metadata = Metadata.new(metadata_path)
|
71
|
+
package_name = metadata.name
|
72
|
+
package_version = metadata.version
|
73
|
+
package_dependencies = metadata.dependencies
|
74
|
+
|
75
|
+
package_path = File.join(temp_dir, "#{package_name}-#{package_version}")
|
76
|
+
FileUtils.mkdir_p(package_path)
|
77
|
+
|
78
|
+
Zip::File.open(file_path) do |zip_file|
|
79
|
+
zip_file.each do |entry|
|
80
|
+
entry_path = File.join(package_path, entry.name)
|
81
|
+
FileUtils.mkdir_p(File.dirname(entry_path))
|
82
|
+
entry.extract(entry_path)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
package_path
|
87
|
+
end
|
88
|
+
|
89
|
+
def load_package
|
90
|
+
# Mandatory
|
91
|
+
@metadata = Metadata.new(metadata_path)
|
92
|
+
|
93
|
+
# Optional
|
94
|
+
@manifest = Manifest.new(manifest_path)
|
95
|
+
@routes = Routes.new(routes_path, @manifest)
|
96
|
+
@storage = Storage.new(storage_path)
|
97
|
+
# @datasets = load_datasets
|
98
|
+
end
|
99
|
+
|
100
|
+
def cleanup
|
101
|
+
return unless @path != @original_path && File.directory?(@path)
|
102
|
+
|
103
|
+
FileUtils.remove_entry(@path)
|
104
|
+
end
|
105
|
+
|
106
|
+
def package_files
|
107
|
+
@packager.package_files
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def create_package_structure
|
113
|
+
FileUtils.mkdir_p(@path)
|
114
|
+
FileUtils.mkdir_p(content_path)
|
115
|
+
FileUtils.mkdir_p(data_path)
|
116
|
+
end
|
117
|
+
|
118
|
+
def content_path
|
119
|
+
File.join(@path, CONTENT_DIR)
|
120
|
+
end
|
121
|
+
|
122
|
+
def data_path
|
123
|
+
File.join(@path, DATA_DIR)
|
124
|
+
end
|
125
|
+
|
126
|
+
def routes_path
|
127
|
+
File.join(@path, ROUTES_FILE)
|
128
|
+
end
|
129
|
+
|
130
|
+
def storage_path
|
131
|
+
File.join(@path, STORAGE_FILE)
|
132
|
+
end
|
133
|
+
|
134
|
+
def metadata_path
|
135
|
+
File.join(@path, METADATA_FILE)
|
136
|
+
end
|
137
|
+
|
138
|
+
def manifest_path
|
139
|
+
File.join(@path, MANIFEST_FILE)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|