{"id":27947884,"url":"https://github.com/luckyframework/lucky_template","last_synced_at":"2025-06-22T03:34:18.729Z","repository":{"id":166467145,"uuid":"640611678","full_name":"luckyframework/lucky_template","owner":"luckyframework","description":"A simple, yet versatile, library for creating file and folder structures as code templates.","archived":false,"fork":false,"pushed_at":"2023-09-29T22:26:00.000Z","size":215,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-07T14:48:58.359Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://luckyframework.github.io/lucky_template/","language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luckyframework.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-05-14T16:58:13.000Z","updated_at":"2023-12-02T20:30:19.000Z","dependencies_parsed_at":"2023-07-04T02:25:02.269Z","dependency_job_id":"2a0f506f-6c4e-422e-9928-90aba812d642","html_url":"https://github.com/luckyframework/lucky_template","commit_stats":null,"previous_names":["mdwagner/lucky_template","luckyframework/lucky_template"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/luckyframework/lucky_template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky_template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky_template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky_template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky_template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luckyframework","download_url":"https://codeload.github.com/luckyframework/lucky_template/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky_template/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261231556,"owners_count":23128019,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-07T14:38:34.569Z","updated_at":"2025-06-22T03:34:13.720Z","avatar_url":"https://github.com/luckyframework.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LuckyTemplate\n\n[![CI](https://github.com/luckyframework/lucky_template/actions/workflows/ci.yml/badge.svg)](https://github.com/mdwagner/lucky_template/actions/workflows/ci.yml)\n\n[LuckyTemplate](https://github.com/luckyframework/lucky_template) is a simple, yet versatile, library for creating file and folder structures as code templates. It has a lot in common with [Teeplate](https://github.com/mosop/teeplate), but it offers more capabilities and more ways to generate content.\n\n## Features\n\n* Allows you to create a virtual file/folder structure, which can be written to disk anywhere.\n* Supports using folders like building blocks, allowing you to reuse folders within other folders.\n* File content can be static or dynamic\n* Includes a Spec helper to validate that the files/folders within a location exist after writing to disk.\n* Provides a snapshot of a folder structure for custom validation or for any other purpose like generating log output.\n\n## Installation\n\n1. Add the dependency to your `shard.yml`:\n\n  ```yaml\n  dependencies:\n    lucky_template:\n      github: luckyframework/lucky_template\n      version: ~\u003e 0.2.0\n  ```\n\n2. Run `shards install`\n\n## Quick Start\n\nHere's a basic example of how to use LuckyTemplate:\n\n```crystal\nrequire \"lucky_template\"\n\nLuckyTemplate.write!(Path[\".\"]) do |folder|\n  folder.add_file(\".keep\")\nend\n```\n\nThis will create a folder at the current directory with an empty `.keep` file. See the sections below for more complex examples.\n\n## Examples\n\n### Simple Template\n\nIn this example, we create a simple README file using an ECR template, a welcome file, and a license file. We also create a new file and folder dynamically in a subfolder.\n\n```crystal\nrequire \"lucky_template\"\n\nclass Readme\n  include LuckyTemplate::Fileable\n\n  def initialize(@name : String)\n  end\n\n  def to_file(io : IO) : Nil\n    to_s(io)\n  end\n\n  ECR.def_to_s \"README.md.ecr\"\nend\n\nname = \"John\"\nfolder = LuckyTemplate.write!(Path[\".\"]) do |dir|\n  dir.add_file(\"README.md\", Readme.new(name))\n\n  dir.add_file(\"Welcome.md\") do |io|\n    io \u003c\u003c \"# Welcome \" \u003c\u003c name \u003c\u003c \"!\\n\"\n  end\n\n  dir.add_file(\"LICENSE\", \u003c\u003c-LICENSE)\n  The MIT License (MIT)\n  ...\n  LICENSE\n\n  dir.add_folder(\"src\") do |src|\n    src.add_file(\".keep\")\n\n    src.add_folder(name.downcase) do |name_dir|\n      name_dir.add_file(\"#{name.downcase}.cr\", \u003c\u003c-CR)\n      class #{name}\n      end\n\n      pp! #{name}.new\n      CR\n    end\n  end\nend\n```\n\n### Creating Multiple Folders\n\nYou can also create multiple folders at once or nest them within other folders:\n\n```crystal\nfolder1 = LuckyTemplate.create_folder do |dir|\n  dir.add_file(\"folder_one.txt\")\nend\nfolder2 = LuckyTemplate.create_folder do |dir|\n  dir.add_file(\"folder_two.txt\")\nend\nfolder3 = LuckyTemplate.create_folder do |dir|\n  dir.add_file(\"folder_three.txt\")\n  dir.insert_folder(\"folder1\", folder1)\n  dir.insert_folder(\"folder2\", folder2)\nend\nLuckyTemplate.write!(Path[\".\"], folder3)\n```\n\n### Snapshot Folders\n\nAnother great feature is that you can take snapshots of folder structures:\n\n```crystal\nsnapshot = LuckyTemplate.snapshot(folder3)\n\nsnapshot.each do |path, type|\n  case type\n  in .file?\n    puts \"File: #{path}\"\n  in .folder?\n    puts \"Folder: #{path}\"\n  end\nend\n```\n\n### Using Non-Crystal Templates\n\nIf you prefer to use another template engine besides Crystal's, you can do so by creating a running process, as shown in the following example with gettext `envsubst`:\n\n```crystal\nclass ExternalTemplate\n  include LuckyTemplate::Fileable\n\n  def initialize(@name : String)\n  end\n\n  def to_file(io : IO) : Nil\n    input = IO::Memory.new(\"Hello $NAME!\")\n    Process.run(\n      command: \"envsubst\",\n      input: input,\n      output: io,\n      env: {\n        \"NAME\" =\u003e @name,\n      }\n    )\n  end\nend\n\nLuckyTemplate.write!(Path[\".\"]) do |dir|\n  dir.add_file(\"external_file\", ExternalTemplate.new(\"John\"))\nend\n```\n\n## FAQ\n\n- Why create this if Teeplate already exists? Initially, I tried to add Windows support to Teeplate, but ran into some issues, that prompted me to just ask the question: Is this effort worth it? [This discussion](https://github.com/luckyframework/lucky/discussions/1812) gave me the push I needed to create a POC (proof-of-concept), which this library is the result of.\n- Does it support Windows? Yes, it runs the spec suite on Linux \u0026 Windows.\n- Why do folder snapshots not contain the file content? Because I haven't found a good enough use-case to support this. Snapshots are supposed to be an escape hatch, where you can get the file/folder structure, but decide what you would like to do with it. Having it include file content doesn't really make sense when LuckyTemplate already writes it to disk for you. But, if you have a good use-case that I haven't thought of, open an issue!\n- Can I use it with `crinja`, `kilt`, `slang`, \u003cinsert templating language\u003e...? Yes, or at least, that's the goal! If you find it doesn't work with something, open an issue.\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/luckyframework/lucky_template/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [Michael Wagner](https://github.com/mdwagner) - creator and maintainer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluckyframework%2Flucky_template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluckyframework%2Flucky_template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluckyframework%2Flucky_template/lists"}