From 0b9227f43d4803ad183789f484006247573a3439 Mon Sep 17 00:00:00 2001 From: Paul Duncan Date: Tue, 19 Jul 2016 18:00:54 -0400 Subject: refactor theme into packer and installer --- src/guff/cli.cr | 2 +- src/guff/theme/file-info.cr | 23 ------- src/guff/theme/installer/file-info.cr | 7 +++ src/guff/theme/installer/manifest.cr | 19 ++++++ src/guff/theme/packer/file-info.cr | 23 +++++++ src/guff/theme/packer/manifest.cr | 113 ++++++++++++++++++++++++++++++++++ src/guff/theme/source-manifest.cr | 113 ---------------------------------- 7 files changed, 163 insertions(+), 137 deletions(-) delete mode 100644 src/guff/theme/file-info.cr create mode 100644 src/guff/theme/installer/file-info.cr create mode 100644 src/guff/theme/installer/manifest.cr create mode 100644 src/guff/theme/packer/file-info.cr create mode 100644 src/guff/theme/packer/manifest.cr delete mode 100644 src/guff/theme/source-manifest.cr diff --git a/src/guff/cli.cr b/src/guff/cli.cr index 15ff5a4..600f5d4 100644 --- a/src/guff/cli.cr +++ b/src/guff/cli.cr @@ -112,7 +112,7 @@ module Guff::CLI class ThemePackAction < Action def run - Theme::SourceManifest.save( + Theme::Packer::Manifest.save( src_dir: @config.theme_src_dir.not_nil!, dst_path: @config.theme_dst_path.not_nil!, ) diff --git a/src/guff/theme/file-info.cr b/src/guff/theme/file-info.cr deleted file mode 100644 index 768d625..0000000 --- a/src/guff/theme/file-info.cr +++ /dev/null @@ -1,23 +0,0 @@ -class Guff::Theme::FileInfo - getter :size, :hash - - def initialize(src_dir : String, @path : String) - # build absolute path - abs_path = File.join(src_dir, "files", @path) - - # get file size - @size = File.size(abs_path) - - # hash file contents - d = OpenSSL::Digest.new("SHA1") - @hash = d.file(abs_path).hexdigest as String - end - - def to_json(io) - { - name: @path, - size: @size, - hash: @hash, - }.to_json(io) - end -end diff --git a/src/guff/theme/installer/file-info.cr b/src/guff/theme/installer/file-info.cr new file mode 100644 index 0000000..dfbd9d8 --- /dev/null +++ b/src/guff/theme/installer/file-info.cr @@ -0,0 +1,7 @@ +class Guff::Theme::Installer::FileInfo + JSON.mapping( + name: String, + size: Int64, + hash: String, + ) +end diff --git a/src/guff/theme/installer/manifest.cr b/src/guff/theme/installer/manifest.cr new file mode 100644 index 0000000..5e7e07f --- /dev/null +++ b/src/guff/theme/installer/manifest.cr @@ -0,0 +1,19 @@ +class Guff::Theme::Installer::Manifest + JSON.mapping( + format: Int32, + metadata: Hash(String, String), + files: Array(FileInfo), + assets: Assets, + templates: Hash(String, String), + ) + + def self.load(zip_path : String) : Manifest + json = "" + Zip::Archive.open(zip_path) do |zip| + json = zip.read("guff-manifest.json") + end + + # return result + from_json(json) + end +end diff --git a/src/guff/theme/packer/file-info.cr b/src/guff/theme/packer/file-info.cr new file mode 100644 index 0000000..c274f01 --- /dev/null +++ b/src/guff/theme/packer/file-info.cr @@ -0,0 +1,23 @@ +class Guff::Theme::Packer::FileInfo + getter :size, :hash + + def initialize(src_dir : String, @path : String) + # build absolute path + abs_path = File.join(src_dir, "files", @path) + + # get file size + @size = File.size(abs_path) + + # hash file contents + d = OpenSSL::Digest.new("SHA1") + @hash = d.file(abs_path).hexdigest as String + end + + def to_json(io) + { + name: @path, + size: @size, + hash: @hash, + }.to_json(io) + end +end diff --git a/src/guff/theme/packer/manifest.cr b/src/guff/theme/packer/manifest.cr new file mode 100644 index 0000000..21fbda7 --- /dev/null +++ b/src/guff/theme/packer/manifest.cr @@ -0,0 +1,113 @@ +class Guff::Theme::Packer::Manifest + getter :format, :metadata, :assets + + JSON.mapping( + format: Int32, + metadata: Hash(String, String), + assets: Assets, + ) + + REQUIRED_METADATA_FIELDS = { + "name" => /\S+/, + "version" => /\S+/, + "date" => /^\d{4}-\d{2}-\d{2}$/, + } + + def self.save(src_dir : String, dst_path : String) + load(src_dir).save(dst_path, src_dir) + end + + def self.load(src_dir : String) : Manifest + r = from_json(File.read(File.join(src_dir, "guff-manifest.json"))) + + # check format version + unless r.format == 1 + raise "unknown theme format: #{r.format}" + end + + # check for required metadata fields + REQUIRED_METADATA_FIELDS.each do |key, re| + unless r.metadata[key]? + # missing field + raise "Missing required metadata field: #{key}" + end + + unless r.metadata[key].match(re) + # invalid field format + raise "Invalid format for metadata field: #{key}" + end + end + + # check assets + { + scripts: r.assets.scripts, + styles: r.assets.styles, + }.each do |type, paths| + # find assets specified in manifest but not in + missing_files = paths.select { |path| + !File.exists?(File.join(src_dir, "files", path)) + } + + if missing_files.size > 0 + raise "Missing #{type}: #{missing_files.join(", ")}" + end + end + + # return result + r + end + + def to_json(io : IO, src_dir : String) + # build directory paths + templates_dir = File.join(src_dir, "templates") + + # return output json + { + # format version + format: 1, + + # theme metadata + metadata: @metadata, + + # theme files + files: files(File.join(src_dir, "files")).map { |path| + FileInfo.new(src_dir, path) + }, + + # theme templates + templates: files(templates_dir).reduce({} of String => String) do |r, path| + r[path] = File.read(File.join(templates_dir, path)) + r + end, + + # theme assets (css and js files) + assets: @assets, + }.to_json(io) + end + + def save(dst_path : String, src_dir : String) + Zip::Archive.create(dst_path) do |zip| + # add manifest + zip.add("guff-manifest.json", String.build do |io| + to_json(io, src_dir) + end) + + # add files + files(File.join(src_dir, "files")).each do |path| + # build path + dst_path = File.join("files", path) + + # add file to archive + zip.add_file(dst_path, File.join(src_dir, dst_path)) + end + end + end + + private def files(src_dir : String) + Dir.glob(File.join(src_dir, "**/*")).select { |path| + !File.directory?(path) + }.map { |path| + path.gsub(src_dir + "/", "") + } + end +end diff --git a/src/guff/theme/source-manifest.cr b/src/guff/theme/source-manifest.cr deleted file mode 100644 index bfa3486..0000000 --- a/src/guff/theme/source-manifest.cr +++ /dev/null @@ -1,113 +0,0 @@ -class Guff::Theme::SourceManifest - getter :format, :metadata, :assets - - JSON.mapping( - format: Int32, - metadata: Hash(String, String), - assets: Assets, - ) - - REQUIRED_METADATA_FIELDS = { - "name" => /\S+/, - "version" => /\S+/, - "date" => /^\d{4}-\d{2}-\d{2}$/, - } - - def self.save(src_dir : String, dst_path : String) - load(src_dir).save(dst_path, src_dir) - end - - def self.load(src_dir : String) : SourceManifest - r = from_json(File.read(File.join(src_dir, "guff-manifest.json"))) - - # check format version - unless r.format == 1 - raise "unknown theme format: #{r.format}" - end - - # check for required metadata fields - REQUIRED_METADATA_FIELDS.each do |key, re| - unless r.metadata[key]? - # missing field - raise "Missing required metadata field: #{key}" - end - - unless r.metadata[key].match(re) - # invalid field format - raise "Invalid format for metadata field: #{key}" - end - end - - # check assets - { - scripts: r.assets.scripts, - styles: r.assets.styles, - }.each do |type, paths| - # find assets specified in manifest but not in - missing_files = paths.select { |path| - !File.exists?(File.join(src_dir, "files", path)) - } - - if missing_files.size > 0 - raise "Missing #{type}: #{missing_files.join(", ")}" - end - end - - # return result - r - end - - def to_json(io : IO, src_dir : String) - # build directory paths - templates_dir = File.join(src_dir, "templates") - - # return output json - { - # format version - format: 1, - - # theme metadata - metadata: @metadata, - - # theme files - files: files(File.join(src_dir, "files")).map { |path| - FileInfo.new(src_dir, path) - }, - - # theme templates - templates: files(templates_dir).reduce({} of String => String) do |r, path| - r[path] = File.read(File.join(templates_dir, path)) - r - end, - - # theme assets (css and js files) - assets: @assets, - }.to_json(io) - end - - def save(dst_path : String, src_dir : String) - Zip::Archive.create(dst_path) do |zip| - # add manifest - zip.add("guff-manifest.json", String.build do |io| - to_json(io, src_dir) - end) - - # add files - files(File.join(src_dir, "files")).each do |path| - # build path - dst_path = File.join("files", path) - - # add file to archive - zip.add_file(dst_path, File.join(src_dir, dst_path)) - end - end - end - - private def files(src_dir : String) - Dir.glob(File.join(src_dir, "**/*")).select { |path| - !File.directory?(path) - }.map { |path| - path.gsub(src_dir + "/", "") - } - end -end -- cgit v1.2.3