{"id":36220300,"url":"https://github.com/servactory/stroma","last_synced_at":"2026-02-15T17:43:57.238Z","repository":{"id":331773181,"uuid":"1128920674","full_name":"servactory/stroma","owner":"servactory","description":"A foundation for building modular, extensible DSLs in Ruby","archived":false,"fork":false,"pushed_at":"2026-02-13T21:27:12.000Z","size":64,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-13T22:52:11.537Z","etag":null,"topics":["dsl","gem","modular","ruby","ruby-dsl","ruby-gem"],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/servactory.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-06T10:43:43.000Z","updated_at":"2026-02-13T15:17:08.000Z","dependencies_parsed_at":"2026-01-28T17:01:45.916Z","dependency_job_id":null,"html_url":"https://github.com/servactory/stroma","commit_stats":null,"previous_names":["servactory/stroma"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/servactory/stroma","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/servactory%2Fstroma","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/servactory%2Fstroma/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/servactory%2Fstroma/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/servactory%2Fstroma/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/servactory","download_url":"https://codeload.github.com/servactory/stroma/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/servactory%2Fstroma/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29483282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T15:33:17.885Z","status":"ssl_error","status_checked_at":"2026-02-15T15:32:53.698Z","response_time":118,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["dsl","gem","modular","ruby","ruby-dsl","ruby-gem"],"created_at":"2026-01-11T04:55:54.199Z","updated_at":"2026-02-15T17:43:57.233Z","avatar_url":"https://github.com/servactory.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://servactory.com\" target=\"_blank\"\u003e\n    \u003cpicture\u003e\n      \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/servactory/stroma/main/.github/logo-dark.svg\"\u003e\n      \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/servactory/stroma/main/.github/logo-light.svg\"\u003e\n      \u003cimg alt=\"Stroma\" src=\"https://raw.githubusercontent.com/servactory/stroma/main/.github/logo-light.svg\" width=\"350\" height=\"70\" style=\"max-width: 100%;\"\u003e\n    \u003c/picture\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  A foundation for building modular, extensible DSLs in Ruby.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://rubygems.org/gems/stroma\"\u003e\u003cimg src=\"https://img.shields.io/gem/v/stroma?logo=rubygems\u0026logoColor=fff\" alt=\"Gem version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/servactory/stroma/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/release-date/servactory/stroma\" alt=\"Release Date\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://rubygems.org/gems/stroma\"\u003e\u003cimg src=\"https://img.shields.io/gem/dt/stroma\" alt=\"Downloads\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.ruby-lang.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/Ruby-3.2+-red\" alt=\"Ruby version\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003c!--\n## 📚 Documentation\n\nSee [stroma.servactory.com](https://stroma.servactory.com) for documentation, including:\n\n- Architecture overview\n- Registry and DSL modules\n- Hooks and extensions\n- Settings hierarchy\n- API reference\n--\u003e\n\n## 💡 Why Stroma?\n\nBuilding modular DSLs shouldn't require reinventing the wheel. Stroma provides a structured approach for library authors to compose DSL modules with:\n\n- 🔌 **Module Registration** - Register DSL modules at boot time, compose them into a unified interface\n- 🧱 **Structured Composition** - Include all registered modules automatically via single DSL entry point\n- 🏛️ **Inheritance Safe** - Per-class state isolation with automatic deep copying\n- 🪝 **Extension Hooks** - Optional before/after hooks for user customization\n- ⚙️ **Extension Settings** - Three-level hierarchical storage for extension configuration\n- 🔒 **Thread Safe** - Immutable registry after finalization, safe concurrent reads\n\n## 🧬 Concept\n\nStroma is a foundation for library authors building DSL-driven frameworks (service objects, form objects, decorators, etc.).\n\n**Core lifecycle:**\n1. **Define** - Create a Matrix with DSL modules at boot time\n2. **Include** - Classes include the matrix's DSL to gain all modules\n3. **Extend** (optional) - Add cross-cutting logic via `before`/`after` hooks\n\n## 🚀 Quick Start\n\n### Installation\n\n```ruby\nspec.add_dependency \"stroma\", \"\u003e= 0.4\"\n```\n\n### Define your library's DSL\n\n```ruby\nmodule MyLib\n  STROMA = Stroma::Matrix.define(:my_lib) do\n    register :inputs, MyLib::Inputs::DSL\n    register :actions, MyLib::Actions::DSL\n  end\n  private_constant :STROMA\nend\n```\n\n### Create base class\n\n```ruby\nmodule MyLib\n  class Base\n    include STROMA.dsl\n  end\nend\n```\n\n### Usage\n\nCreate an intermediate class with lifecycle hooks:\n\n```ruby\nclass ApplicationService \u003c MyLib::Base\n  # Add lifecycle hooks (optional)\n  extensions do\n    before :actions, ApplicationService::Extensions::Rollbackable::DSL\n  end\nend\n```\n\nBuild services that inherit extension functionality:\n\n```ruby\nclass UserService \u003c ApplicationService\n  # DSL method from Rollbackable extension\n  on_rollback(...)\n\n  input :email, type: String\n\n  make :create_user\n\n  private\n\n  def create_user\n    # implementation\n  end\nend\n```\n\nExtensions allow you to add cross-cutting concerns like transactions, authorization, and rollback support. See [extension examples](https://github.com/servactory/servactory/tree/main/examples/application_service/extensions) for implementation details.\n\n## 🧩 Building Extensions\n\nExtensions are standard Ruby modules that hook into the DSL lifecycle. Stroma places them at the correct position in the method chain, so `super` naturally flows through all registered extensions.\n\n### Define an extension\n\n```ruby\nmodule Authorization\n  def self.included(base)\n    base.extend(ClassMethods)\n    base.include(InstanceMethods)\n  end\n\n  module ClassMethods\n    def authorize_with(method_name)\n      stroma.settings[:actions][:authorization][:method_name] = method_name\n    end\n  end\n\n  module InstanceMethods\n    def call(...)\n      method_name = self.class.stroma.settings[:actions][:authorization][:method_name]\n      send(method_name) if method_name\n      super\n    end\n  end\nend\n```\n\n`ClassMethods` provides the class-level DSL. `InstanceMethods` overrides the orchestrator method defined by your library (here `call`) and delegates via `super`. Split them into separate files as the extension grows.\n\n### Register the extension\n\n```ruby\nclass ApplicationService \u003c MyLib::Base\n  extensions do\n    before :actions, Authorization\n  end\nend\n```\n\n`before` places the module so its `call` executes **before** the `:actions` entry. Use `after` for post-processing. Multiple modules in one call: `before :actions, ModA, ModB`.\n\n### Use in a service\n\n```ruby\nclass UserService \u003c ApplicationService\n  authorize_with :check_permissions\n\n  input :email, type: String\n\n  make :create_user\n\n  private\n\n  def check_permissions\n    # authorization logic\n  end\n\n  def create_user\n    # runs only after check_permissions passes\n  end\nend\n```\n\nSettings and hooks are deep-copied on inheritance — each subclass has independent configuration.\n\n## 💎 Projects Using Stroma\n\n- [Servactory](https://github.com/servactory/servactory) — Service objects framework for Ruby applications\n\n## 🤝 Contributing\n\nWe welcome contributions! Check out our [Contributing Guide](https://github.com/servactory/stroma/blob/main/CONTRIBUTING.md) to get started.\n\n**Ways to contribute:**\n- 🐛 Report bugs and issues\n- 💡 Suggest new features\n- 📝 Improve documentation\n- 🧪 Add test cases\n- 🔧 Submit pull requests\n\n## 🙏 Acknowledgments\n\nSpecial thanks to all our [contributors](https://github.com/servactory/stroma/graphs/contributors)!\n\n## 📄 License\n\nStroma is available as open source under the terms of the [MIT License](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fservactory%2Fstroma","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fservactory%2Fstroma","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fservactory%2Fstroma/lists"}