{"id":18721756,"url":"https://github.com/pedro-gutierrez/diesel","last_synced_at":"2025-09-11T01:09:16.313Z","repository":{"id":198182905,"uuid":"700271874","full_name":"pedro-gutierrez/diesel","owner":"pedro-gutierrez","description":"DSL craft and declarative programming in Elixir","archived":false,"fork":false,"pushed_at":"2025-07-27T10:01:38.000Z","size":109,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-26T02:19:38.687Z","etag":null,"topics":["ast","declarative","dsl","elixir","macros"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/diesel","language":"Elixir","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/pedro-gutierrez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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-10-04T09:26:13.000Z","updated_at":"2025-07-27T11:51:26.000Z","dependencies_parsed_at":"2023-12-10T12:22:07.724Z","dependency_job_id":"a593767e-1762-4bcf-b6ed-26bbaff1abf4","html_url":"https://github.com/pedro-gutierrez/diesel","commit_stats":{"total_commits":42,"total_committers":1,"mean_commits":42.0,"dds":0.0,"last_synced_commit":"36f3f47648258ee9d8d672bf439f874da0d1151c"},"previous_names":["pedro-gutierrez/diesel"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/pedro-gutierrez/diesel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pedro-gutierrez%2Fdiesel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pedro-gutierrez%2Fdiesel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pedro-gutierrez%2Fdiesel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pedro-gutierrez%2Fdiesel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pedro-gutierrez","download_url":"https://codeload.github.com/pedro-gutierrez/diesel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pedro-gutierrez%2Fdiesel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274557583,"owners_count":25307516,"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","status":"online","status_checked_at":"2025-09-10T02:00:12.551Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["ast","declarative","dsl","elixir","macros"],"created_at":"2024-11-07T13:36:39.566Z","updated_at":"2025-09-11T01:09:16.285Z","avatar_url":"https://github.com/pedro-gutierrez.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Diesel\n\n[![Hex.pm](https://img.shields.io/hexpm/v/diesel.svg)](https://hex.pm/packages/diesel)\n[![Documentation](https://img.shields.io/badge/docs-hexpm-blue.svg)](https://hexdocs.pm/diesel)\n\n**Diesel** is a powerful toolkit for building Domain Specific Languages (DSLs) in Elixir. Create declarative, structured, and reusable DSLs with compile-time validation and flexible code generation.\n\n## Features\n\n- **Declarative**: Clean, HTML-like syntax that's easy to read and write\n- **Structured**: Compile-time schema validation ensures correctness  \n- **Reusable**: Same DSL can power multiple code generators\n- **Flexible**: Support for custom parsers, generators, and tag definitions\n- **Type-safe**: Rich attribute validation with kinds, constraints, and defaults\n\n## Installation\n\nAdd `diesel` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:diesel, \"~\u003e 0.8\"}\n  ]\nend\n```\n\n## Quick Start\n\nHere's an example of building a database schema DSL:\n\n### 1. Define your DSL library\n\n```elixir\ndefmodule MyApp.Schema do\n  use Diesel, otp_app: :my_app\nend\n```\n\n### 2. Define the DSL structure\n\n```elixir\ndefmodule MyApp.Schema.Dsl do\n  use Diesel.Dsl,\n    otp_app: :my_app,\n    tags: [\n      MyApp.Schema.Dsl.Table,\n      MyApp.Schema.Dsl.Column,\n      MyApp.Schema.Dsl.Index,\n      MyApp.Schema.Dsl.Relationship,\n      MyApp.Schema.Dsl.Validation\n    ]\nend\n```\n\n### 3. Define your tags\n\n```elixir\ndefmodule MyApp.Schema.Dsl.Table do\n  use Diesel.Tag\n\n  tag do\n    attribute :name, kind: :atom\n    attribute :primary_key, kind: :atom, default: :id\n    child :column, min: 1\n    child :index, min: 0\n    child :relationship, min: 0\n  end\nend\n\ndefmodule MyApp.Schema.Dsl.Column do\n  use Diesel.Tag\n\n  tag do\n    attribute :name, kind: :atom\n    attribute :type, kind: :atom, \n              one_of: [:string, :integer, :boolean, :datetime, :decimal, :text]\n    attribute :null, kind: :boolean, default: true\n    attribute :unique, kind: :boolean, default: false\n    attribute :size, kind: :integer, required: false\n    child :validation, min: 0\n  end\nend\n\ndefmodule MyApp.Schema.Dsl.Relationship do\n  use Diesel.Tag\n\n  tag do\n    attribute :type, kind: :atom, one_of: [:belongs_to, :has_many, :has_one]\n    attribute :name, kind: :atom\n    attribute :target, kind: :atom\n    attribute :foreign_key, kind: :atom, required: false\n  end\nend\n```\n\n### 4. Use your DSL\n\n```elixir\ndefmodule MyApp.UserSchema do\n  use MyApp.Schema\n\n  database do\n    table :users do\n      column :email, type: :string, null: false, unique: true do\n        validation :format, pattern: ~r/@/\n        validation :length, min: 5, max: 100\n      end\n      \n      column :name, type: :string, null: false, size: 255 do\n        validation :length, min: 2, max: 50\n      end\n      \n      column :age, type: :integer do\n        validation :range, min: 13, max: 120\n      end\n      \n      column :created_at, type: :datetime, null: false\n      column :updated_at, type: :datetime, null: false\n      \n      index [:email], unique: true\n      index [:created_at]\n      \n      relationship :has_many, :posts, target: :posts, foreign_key: :user_id\n      relationship :has_one, :profile, target: :profiles\n    end\n\n    table :posts do\n      column :title, type: :string, null: false, size: 200 do\n        validation :length, min: 5, max: 200\n      end\n      \n      column :content, type: :text, null: false\n      column :published, type: :boolean, default: false\n      column :user_id, type: :integer, null: false\n      \n      index [:user_id]\n      index [:published, :created_at]\n      \n      relationship :belongs_to, :user, target: :users\n    end\n  end\nend\n```\n\n## Core Concepts\n\n### Tags\n\nTags are the building blocks of your DSL. Each tag can have:\n\n- **Attributes**: Named parameters with type validation\n- **Children**: Nested tags with cardinality constraints  \n- **Content**: Raw values or modules\n\n```elixir\ndefmodule MyDsl.Tag do\n  use Diesel.Tag\n\n  tag do\n    attribute :name, kind: :atom, required: true\n    attribute :size, kind: :integer, min: 1, max: 1000\n    child :nested_tag, min: 0, max: 5\n    child kind: :module, min: 1, max: 1  # unnamed children\n  end\nend\n```\n\n### Parsers\n\nTransform your DSL definition into custom data structures:\n\n```elixir\ndefmodule MyApp.Schema.Parser do\n  @behaviour Diesel.Parser\n\n  def parse(definition, _opts) do\n    # Transform table definitions into Schema structs\n    tables = extract_tables(definition)\n    relationships = extract_relationships(definition)\n    \n    # Return enhanced definition with parsed data\n    definition\n    |\u003e put_in([:parsed_tables], tables)\n    |\u003e put_in([:relationships], relationships)\n  end\n\n  defp extract_tables({:database, _attrs, children}) do\n    children\n    |\u003e Enum.filter(\u0026match?({:table, _attrs, _children}, \u00261))\n    |\u003e Enum.map(\u0026parse_table/1)\n  end\nend\n```\n\n### Generators\n\nGenerate code from your parsed DSL:\n\n```elixir\ndefmodule MyApp.Schema.EctoGenerator do  \n  @behaviour Diesel.Generator\n\n  def generate(definition, _opts) do\n    tables = get_in(definition, [:parsed_tables])\n    \n    quote do\n      # Generate Ecto schema modules\n      unquote_splicing(generate_schemas(tables))\n      \n      # Generate migration functions\n      def migration_up do\n        unquote_splicing(generate_create_tables(tables))\n      end\n    end\n  end\n\n  defp generate_schemas(tables) do\n    Enum.map(tables, fn table -\u003e\n      quote do\n        defmodule unquote(Module.concat(__MODULE__, table.name)) do\n          use Ecto.Schema\n          \n          schema unquote(Atom.to_string(table.name)) do\n            unquote_splicing(generate_fields(table.columns))\n          end\n        end\n      end\n    end)\n  end\nend\n```\n\n## Attribute Types\n\nDiesel supports rich attribute validation:\n\n```elixir\nattribute :name, kind: :atom                    # Basic types\nattribute :size, kind: :integer, min: 1, max: 255      # Numeric constraints  \nattribute :type, kind: :atom, one_of: [:string, :integer]  # Enums\nattribute :options, kind: :keyword_list          # Complex types\nattribute :*, kind: :*                          # Generic attributes (0.8.0+)\n```\n\n## Advanced Features\n\n- **Kernel Conflicts**: Handle conflicts with Kernel functions using `:overrides`\n- **Debug Mode**: Inspect generated code during compilation with `debug: true`\n- **Custom Naming**: Override default module naming conventions\n- **Multiple Parsers**: Chain multiple parsers for complex transformations\n- **Conditional Generation**: Generate different code based on parsed data\n\nExample with advanced options:\n\n```elixir\ndefmodule MyApp.Schema do\n  use Diesel,\n    otp_app: :my_app,\n    overrides: [import: 2],  # Handle kernel conflicts\n    parsers: [\n      MyApp.Schema.Parser,\n      MyApp.Schema.ValidationParser,\n      MyApp.Schema.RelationshipParser\n    ],\n    generators: [\n      MyApp.Schema.EctoGenerator,\n      MyApp.Schema.MigrationGenerator,\n      MyApp.Schema.GraphQLGenerator\n    ]\nend\n```\n\n## Documentation\n\nFor detailed guides and examples:\n\n- [Installation Guide](https://hexdocs.pm/diesel/installation.html)\n- [Tutorial](https://hexdocs.pm/diesel/tutorial.html) \n- [Parsers Guide](https://hexdocs.pm/diesel/parsers.html)\n- [Generators Guide](https://hexdocs.pm/diesel/generators.html)\n- [API Reference](https://hexdocs.pm/diesel/api-reference.html)\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpedro-gutierrez%2Fdiesel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpedro-gutierrez%2Fdiesel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpedro-gutierrez%2Fdiesel/lists"}