{"id":13878639,"url":"https://github.com/motine/dsl_factory","last_synced_at":"2025-07-16T14:32:36.841Z","repository":{"id":37747091,"uuid":"464896652","full_name":"motine/dsl_factory","owner":"motine","description":"Define DSLs quickly and avoid the boilerplate write getters and setters. Oh, and it does validation too.","archived":false,"fork":false,"pushed_at":"2023-08-23T11:38:53.000Z","size":15,"stargazers_count":47,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-31T14:54:20.234Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/motine.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-03-01T13:06:24.000Z","updated_at":"2023-08-23T10:48:17.000Z","dependencies_parsed_at":"2024-01-13T20:36:28.358Z","dependency_job_id":"8ef71335-559f-45ac-8cdc-ceba6044a302","html_url":"https://github.com/motine/dsl_factory","commit_stats":{"total_commits":3,"total_committers":1,"mean_commits":3.0,"dds":0.0,"last_synced_commit":"f4d06a0233a4bb3e9a9f6ee8b6b5a6ccbd294aed"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motine%2Fdsl_factory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motine%2Fdsl_factory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motine%2Fdsl_factory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motine%2Fdsl_factory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/motine","download_url":"https://codeload.github.com/motine/dsl_factory/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226138849,"owners_count":17579496,"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":"2024-08-06T08:01:55.456Z","updated_at":"2024-11-24T07:31:11.660Z","avatar_url":"https://github.com/motine.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# DSL Factory\n\nA small DSL to generate DSLs.  \nDefine DSLs quickly and avoid the [boilerplate to write getters and setters](https://gist.github.com/motine/28da503ba0075e9d64d3f3b1faab9014). Oh, and it does validation too.\n\n## Example\n\n```ruby\n# add this to your Gemfile: gem 'dsl_factory'\n\n# define the DSL\nLakeDsl = DslFactory.define_dsl do\n  string :lake_name\n  numeric :max_depth\n  array :fishes, String\nend\n\n# use it in any class\nclass LakeSuperior\n  extend LakeDsl\n\n  lake_name 'Lake Superior'\n  max_depth 406\n  fish 'trout'\n  fish 'northern pike'\nend\n\n# and you can access the values\nLakeSuperior.lake_name # =\u003e \"Lake Superior\"\nLakeSuperior.fishes # =\u003e [\"trout\", \"northern pike\"]\n```\n\nThis gem came about during my time at [Netskin GmbH](https://www.netskin.com). Check it out, we do great (Rails) work there.\n\n# Usage\n\n## Definition\n\n**Basic Types**\n\n```ruby\n# the following data types are available\n# if a value is given which does not fit the type a `DslFactory::ValidationError` is raised\nLakeDsl = DslFactory.define_dsl do\n  string :lake_name\n  symbol :group\n  numeric :max_depth\n  boolean :protected_habitat\n  callable :current_temperature_handler\n  any :continent # does not validate the given contents later\nend\n\n# now we can use the definition\nclass LakeSuperior\n  extend LakeDsl\n\n  lake_name 'Lake Superior'\n  group :great_lakes\n  max_depth 406\n  protected_habitat true\n  current_temperature_handler -\u003e() { self.temperature = FetchService.receive_temperature } # the proc is only saved, DslFactory will not call it\n  continent Continent::NorthAmerica\nend\n```\n\n**Arrays**\n\n```ruby\nBookDsl = DslFactory.define_dsl do\n  array :authors            # we must use the plural!\n  array :publishers, String # validates that the item is of the given (Ruby) class\n  # see below for nested DSLs\nend\n\nclass SuperBook\n  extend BookDsl\n  authors ['Manfred', 'Dieter'] # we can use the plural form to set the whole array\n  author 'Heinz'                # or the singular form to add an item\n\n  publisher 'abc'\nend\n\nSuperBook.authors # =\u003e ['Manfred', 'Dieter', 'Heinz']\nSuperBook.publishers # =\u003e ['abc']\n```\n\n**Hashes**\n\n```ruby\nGeographyDsl = DslFactory.define_dsl do\n  hash :capital_for_countries         # we must use the plural!\n  hash :country_sizes, String, Numeric # validate key and value (key must be of String class, value of Symbol) \n  # see below for nested DSLs\nend\n\nclass World\n  extend GeographyDsl\n  capital_for_country 'Berlin', 'Germany' # here we use the signular\n  capital_for_country 'Copenhagen', 'Denmark'\n\n  country_size 'Germany', 357_022\nend\n\nWorld.capital_for_countries # =\u003e { 'Berlin' =\u003e 'Germany', 'Copenhagen' =\u003e 'Denmark'}\nWorld.country_sizes # =\u003e { 'Germany' =\u003e 357022 }\n```\n\n**Nested DSLs**\n```ruby\nPersonDsl = DslFactory.define_dsl do\n  array :parents do\n    string :name\n    numeric :age\n  end\n\n  hash :citizenships, String do # validate key as String\n    symbol :status\n    any :expiry\n  end\nend\n\nclass Sabine\n  extend PersonDsl\n  # note that nested DSLs can only be used via the singular form\n\n  parent do\n    name 'Karla'\n    age 88\n  end\n\n  citizenship 'Germany' do\n    status :revoked\n    expiry Time.new(2000)\n  end\nend\n\nSabine.parents.first.name # =\u003e 'Karla'\nSabine.citizenships['Germany'].status # =\u003e :revoked\n```\n\n**Callbacks**\n\nSometimes we might want to do something when the DSL method is called.\nThis can be achived via callbacks.\n\n```ruby\nButtonDsl = DslFactory.define_dsl do\n  # all types outlined above support callbacks\n  any :trigger, callback: -\u003e(value) { puts \"#{value} was triggered\" }\n  any :snicker, callback: -\u003e(value) { arg1, arg2 = value; puts \"snicker: #{arg1} \u0026 #{arg2}\" } # we can pass arguments via the value\n  any :clicker, callback: -\u003e(value) { self.do_the_click } # see method definition in using class\n  numeric :width, callback: -\u003e(value) { raise DslFactory::ValidationError, 'buttons must be small' if width \u003e 100 }\n\n  # for arrays the callback always receives an array (even if it was used in singular form)\n  # for hashes the callback receives two arguments: key, value\nend\n\nclass MonsterButton\n  extend ButtonDsl\n  # if we want to call a method of the class, we need to define it before the first usage of the DSL method\n  def self.do_the_click\n    puts 'Click!'\n  end\n\n  trigger 'abc'            # -\u003e abc was triggered\n  snicker ['haha', 'hihi'] # -\u003e snicker: haha \u0026 hihi\n  clicker nil              # make sure to alway pass a value; -\u003e Click!\n  width 2000               # -\u003e DslFactory::ValidationError: buttons must be small\nend\n\nMonsterButton.trigger # values are still set; =\u003e 'abc'\n```\n\n**Inspection**\n\nFor debugging it is useful to introspect the DSL values.\nWhen `inspectable` is set to `true` (`DslFactory.define_dsl(inspectable: true)`) the module will provide an `inspect` method.\nAll sub-DSL modules provide an such a method by default.\n\n## Use of the definition\n\nUsually we **extend a class** like so:\n\n```ruby\nclass LakeSuperior\n  extend LakeDsl\n  lake_name 'Lake Superior'\nend\n\nLakeSuperior.lake_name # =\u003e 'Lake Superior'\n```\n\nHowever we can also use the **DSL in a variable**:\n\n```ruby\n@config = Module.new.extend(LakeDsl)\n@config.lake_name 'Müritz'\n@config.lake_name # =\u003e 'Müritz'\n\n# or with the configuration pattern\n@config = Module.new.extend(LakeDsl).tap do |c|\n  c.lake_name 'Summter See'\nend\n@config.lake_name # =\u003e 'Summter See'\n\n# or even without any prefix\n@config = Module.new.extend(LakeDsl)\n@config.instance_exec do\n  lake_name 'Mühlenbecker See'\nend\n@config.lake_name # =\u003e 'Mühlenbecker See'\n\n```\n\n# Compatibility\n\nThe gem was/is used in production with the following Ruby versions:\n\n- ✅ Ruby 2.7\n- ✅ Ruby 3.0\n- ✅ Ruby 3.1\n\n# Development\n\n```bash\ndocker run --rm -ti -v (pwd):/app -w /app ruby:2.7 bash\nbundle install\nrake test # run the tests\npry # require_relative 'lib/dsl_factory.rb'\n\n# to release a new version, update `CHANGELOG.md` and the version number in `version.rb`, then run\nbundle exec rake release\n```\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n# Alternatives\n\n_Download counts are from 01.03.2022._\n\n- [dslh](https://github.com/kumogata/dslh) (178.000 downloads)\n  Allows to define a Hash as a DSL.\n- [dsl_maker](https://rubygems.org/gems/dsl_maker) (80.700 downloads)\n    allows defining DSLs with structs.\n- [genki-dsl_accessor](https://rubygems.org/gems/genki-dsl_accessor) (5.600 downloads)\n    allows defining hybrid accessors for class.\n- [configuration_dsl](https://rubygems.org/gems/configuration_dsl) (4.100 downloads)\n    nice way to define configuration DSLs. quite outdated.\n- [blockenspiel](https://github.com/dazuma/blockenspiel) (2.869.000 downloads)\n    allows to nicely define configuration blocks. does not facilitate assigning variables.\n- [dsl_accessors](https://rubygems.org/gems/dsl_accessors) (3.700 downloads)\n    provides helpers to facilitate variable setting via DSL. exactly what we need, but unfortunately quite outdated.\n- [open_dsl](https://rubygems.org/gems/open_dsl) (17.000 downloads)\n    dynamically defines OpenStructs to collect data for DSLs. really nice idea and quite close to what we need. unfortunately a little outdated.\n- [alki-dsl](https://rubygems.org/gems/alki-dsl) (12.900 downloads)\n    allows to define DSL methods nicely. does not assist in variable getting/setting.\n\n**Honerable Mentions**\n\n- [cleanroom](https://rubygems.org/gems/cleanroom) (5.560.000 downloads)\n    allows to safely define DSLs. does not really facilitate the data assignments.\n- [dsl_block](https://rubygems.org/gems/dsl_block) 4.900 downloads)\n    allows you to use classes to define blocks with commands. does not really facilitate setting/getting variables.\n- [declarative](https://rubygems.org/gems/declarative) (110.990.000 downloads)\n    define declarative schemas.\n- [dslkit](https://rubygems.org/gems/dslkit) (54.500 downloads)\n    allows defining DSL to be read from files (maybe). documentation hard to find. outdated.\n- [dsltasks](https://rubygems.org/gems/dsltasks) (4.100 downloads)\n    allows to define hierarchical DSLs. does not facilitate variable assignments.\n- [opendsl](https://rubygems.org/gems/opendsl) (8.800 downloads)\n    allows to simply define DSLs. does not help assigning variables. outdated.\n\n**Not Applicable**\n\n- [dsl](https://rubygems.org/gems/dsl) defines delegators. i am not sure when this is useful.\n- [dsl_eval](https://rubygems.org/gems/dsl_eval) only defines an alias for instance_eval.\n- [instant_dsl](https://rubygems.org/gems/instant_dsl) could not find repo/docs. quite outdated.\n- [dsl_companion](https://rubygems.org/gems/dsl_companion) no documentation.\n- [def_dsl](https://rubygems.org/gems/def_dsl) no documentation.\n- [dslr](https://rubygems.org/gems/dslr) no documentation.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmotine%2Fdsl_factory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmotine%2Fdsl_factory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmotine%2Fdsl_factory/lists"}