{"id":19665459,"url":"https://github.com/changemewtf/grimoire","last_synced_at":"2025-10-08T08:36:51.220Z","repository":{"id":141007890,"uuid":"81684841","full_name":"changemewtf/grimoire","owner":"changemewtf","description":"components and patterns for general server-side wizardry","archived":false,"fork":false,"pushed_at":"2017-02-26T03:09:08.000Z","size":20,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-10T02:33:27.620Z","etag":null,"topics":["general-purpose","ruby","tools"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/changemewtf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2017-02-11T21:52:40.000Z","updated_at":"2017-07-28T16:56:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"782659a2-9083-47da-aed9-6838c9e9b65a","html_url":"https://github.com/changemewtf/grimoire","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/changemewtf%2Fgrimoire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/changemewtf%2Fgrimoire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/changemewtf%2Fgrimoire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/changemewtf%2Fgrimoire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/changemewtf","download_url":"https://codeload.github.com/changemewtf/grimoire/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240974827,"owners_count":19887330,"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":["general-purpose","ruby","tools"],"created_at":"2024-11-11T16:22:51.930Z","updated_at":"2025-10-08T08:36:46.181Z","avatar_url":"https://github.com/changemewtf.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Grimoire\n\n\u003e **grimoire** (n.) /grimˈwɑr/\n\u003e a manual of magic or witchcraft used by witches and sorcerers\n\nThis gem is meant to be used as a personal extension to the standard library.\n\nThat is, I install it as a dependency on all of my projects, so that my tools\nare never more than a `require` statement away. Therefore, this package should\ninclude only features that I personally find useful enough to be universally\navailable throughout my projects.\n\nTo achieve this pattern, I use a script to create new projects that copies a\ndefault Gemfile into the directory:\n\n```ruby\n# frozen_string_literal: true\nsource \"https://rubygems.org\"\n\ngem \"rspec\"\ngem \"pry\"\ngem \"pry-byebug\"\ngem \"grimoire\", github: \"mcantor/grimoire\", branch: \"master\"\n```\n\nHowever, since I want to be able to actively develop `grimoire` as I use it,\nI configure Bundler to build from my local checkout:\n\n```sh\nbundle config local.grimoire /Users/mcantor/src/grimoire\n```\n\nThis way, when I update the package, I can simply bump the gemspec version,\ncommit locally, and run `bundle` to start using the new features.\n\n# Features\n\n## Data Directories\n\nEasy config/test data loader. Currently supports YAML only.\n\n```ruby\nrequire \"grimoire/data_dir\"\n\nclass Application\n  data_dir\n\n  def print_names\n    puts names.join \", \" #=\u003e \"Wash, Book\"\n  end\nend\n\n```\n\n```yaml\n# data/names.yaml\n- Wash\n- Book\n```\n\n## Data Objects\n\nStatic, behavior-less objects that hold information and\nsupport multiple convenient types of access.\n\nVery useful when combined with Data Directories.\n\n```ruby\nrequire \"grimoire/data_objects\"\n\nfood = DataObject.new hey: \"you\"\np food[:hey] #=\u003e \"you\"\np food[\"hey\"] #=\u003e \"you\"\np food.hey #=\u003e \"you\"\n```\n\n## No-Op\n\nIdentity function for getting rid of if statements\n\n```ruby\nrequire \"grimoire/noop\"\n\nbomb = \"hello I am a water balloon please\"\nprint \"Will you [explode] the balloon or [chill]? \u003e\"\ninput = gets.chomp\n\np {\n  \"explode\" =\u003e lambda { \"KABOOM!\" },\n  \"chill\" =\u003e NOOP\n}[input].call(bomb)\n```\n\n## Smart ARGV\n\nSchemaless ARGV interaction for simple scripts.\n\n```ruby\nrequire \"grimoire/smart_argv\"\n\n# script.rb -ari git --file foo.yaml\nARGV.flag :a #=\u003e true\nARGV.flag :r #=\u003e true\nARGV.flag :read #=\u003e true (first character, would also return true if --read passed)\nARGV.option :ignore #=\u003e \"git\"\nARGV.option :file #=\u003e \"foo.yaml\"\n```\n\n## Smart Deindent\n\nNormalize indents of nested heredocs.\n\n```ruby\nrequire \"grimoire/smart_deindent\"\n\nmodule Application\n  GREETING = \u003c\u003c-EOF.smart_deindent\n    This line will have 0 indent. It defines the indent level of the heredoc.\n      This line will have 2 spaces of its indent preserved.\n    Back to 0.\n  EOF\nend\n```\n\n## Smart Variables\n\nDefine and set positional constructor parameters with duck-typed contracts.\n\n```ruby\nrequire \"grimoire/smart_variables\"\n\nclass Recipe\n  smart_variables do\n    Text RO Required name\n    List RW Optional ingredients\n    Date RO Optional pub_date\n  end\nend\n\nrecipe = Recipe.new \"Cake\"\nrecipe.name #=\u003e \"Cake\"\nrecipe.ingredients #=\u003e []\nrecipe.pub_date #=\u003e Date.today\n```\n\nEach smart variable declaration has four parts:\n\n- **Contract**: One of `Text`, `List`, `Date`, `Time`, `Epoc`, `Int`, `Hash`\n- **Permissions**: `RO` =\u003e `attr_reader`, `RW` =\u003e `attr_accessor`\n- **Arity**: `Required` results in `ArgumentError` if not specified. `Optional` sets default by contract\n- **Name**: Snake case, please!\n\nSome contracts support smart typecasting:\n\n```ruby\nrecipe.pub_date = \"11/22\" #=\u003e #\u003cDate: 2017-11-22 ((2458080j,0s,0n),+0s,2299161j)\u003e\n```\n\n## Bash Styles\n\nReusable shell styles.\n\n```ruby\nrequire \"grimoire/bash_styles\"\n\nmessage = \"You must construct additional pylons.\"\nstyle = Style.new [\"fg light red\", \"bold\"]\nputs style.wrap(message)\n```\n\n- **ANSI Colors**:\n  ```ruby\n  Style.new [\"fg blue\", \"bg light red\"]\n  ```\n- **Bold**:\n  ```ruby\n  Style.new [\"bold\"]\n  ```\n- **RGB**:\n  ```ruby\n  Style.new [\"fg 0 0 0\", \"bg 5 5 5\"]` # (black on white)\n  ```\n- **Grayscale**:\n  ```ruby\n  Style.new [\"fg gray 0\", \"bg gray 20\"]` # (darkest on lightest)\n  ```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchangemewtf%2Fgrimoire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchangemewtf%2Fgrimoire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchangemewtf%2Fgrimoire/lists"}