-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create DSL to Ease Creation of End User DSLs #47
Comments
In the WIP I have this API for Entities so far: entity do
name :my_entity
target MyEntity
schema do
field :field_name do
type :any
required true
end
field :other_field do
type :any
required true
end
end
end I use sub-entities for Schema and Field to enable this, Since they are entities in this implementation, it's easy to have default value that are defined in the schema do
field :my_field
field :my_other_field do
required true
end
end Which would work by setting every option field in the Working from nimble_schema: field :my_field do
type :my_type # required true?
required true # required true?
default :any # required false
keys nil # required false (what does keys do)
deprecated "a string" # required false (is this a version number?)
doc "my cool field" # required false (does this map to the `describe` field in an Entity? if so, do we want to rename it to `describe` in the DSL?)
subsection "" # required false (What is a subsection in a nimble_schema?)
type_doc false # required false (What does this do?)
rename_to :another_field_name # required false (Is this relevant when we're defining the field name already?)
end Do we want to make any of these fields private? |
The type of |
Consideration: the current syntax requires (lightweight) dependency resolution. In the current syntax, I allow entities and sections to be declared at the top level, and then referred to by their atom name: entity do
name :my_entity
...
entities [entity_field: :my_other_entity]
end
entity do
name :my_other_entity
...
end
section do
name :my_section
...
entities [:my_entity]
end
section do
name :my_other_section
sections [:my_section]
...
end This has the benefit of allowing re-use of entities and sections, but at the cost of needing to resolve all the atom names into their respective entities and sections. But an alternative syntax might look like: section do
name :my_section
sections do
section do
name :my_other_section
entities do
entity do
...
end
end
end
end
end This would avoid the resolution process, since the dependencies are explicit, at the cost of needing to define sections and entities recursively in the implementation. Further this syntax leads to significant nesting, damaging legibility. |
My understanding of the resolution process:
The difficulty comes the "Global" part of the replace. For example: entity do
name :entity1
entities [:entity2]
end
entity do
name :entity2
entities [:entity3]
end
entity do
name :entity3
end Would generate as: [
%Entity{name: :entity1, entities: [:entity2]},
%Entity{name: :entity2, entities: [:entity3]},
%Entity{name: :entity3},
] Which would need to resolve to: %Entity{
name: :entity1
entities: [
%Entity{
name: :entity2,
entities: [%Entity{name: :entity3}]
}
} |
When using |
So I wrote one. #48 in the most recent commit has support for nested entities and nested sections. I added |
I think the atom resolution works, but that there may be some interesting challenges there. For example, if two entities have a nested entity w/ the same name but a different definition. That sounds solvable (like maybe we actually use “id” as a reference instead of the name? Or allow an id to be set and referred to optionally?) |
I see what you mean. When using module attributes, we avoid this by the ability to name the module attribute anything, not just what the name of the entity or section ends up being.
Perhaps Thoughts? |
Just to get a sense of what that look like: entity do
name :my_entity
alias :entity1
end
entity do
name :my_entity
alias :entity2
end
entity do
name :other_entity
entities [sub_entity: :entity1]
end
section do
name :my_section
entities [:entity2, :other_entity]
end Okay |
Perhaps |
hmm.... |
I like that better than
|
Alternatively: |
We could also use |
Let's do I'll move forward with that, but feel free to change your mind before we merge the feature. :) |
To make my plans concrete:
|
With my latest commit #48 now supports:
That's assuming that There isn't really validation yet, but that's not significantly different than manually constructing the Entity and Section structs. |
I'm working on re-implementing the tests for Spark using the meta-DSL, and I came across this: @step %Spark.Dsl.Entity{
name: :step,
target: Spark.Test.Step,
args: [:name],
recursive_as: :steps,
entities: [
steps: []
],
schema: [
name: [
type: :atom,
required: true
],
number: [
type: :integer
]
]
} I want to specifically draw attention to the |
yeah, it is a valid construction. you're right that it's used for |
I see, that's a good clarification. |
Question: rather than: sections = Spark.load(Impl)
use Spark.Dsl.Extension,
sections: sections Is it possible to do something like this instead? use Spark.Dsl.Extension do
entity ...
end _Originally posted by @jimsynz in #48 (comment)
We probably can't do
Since I would need to investigate what would be required to provide that API. Originally posted by @erikareads in #48 (comment) |
I think it's worth asking what a radically improved API for Right now: defmodule MyThing.Dsl do
@entity %Spark.Dsl.Entity{...}
@section %Spark.Dsl.Section{...}
@moduledoc """
#{Spark.Dsl.Extension.doc_index([@section])}
#{Spark.Dsl.Extension.doc([@section])}
"""
use Spark.Dsl.Extension,
sections: @sections,
transformers: @transformers,
verifiers: @verifiers
end
defmodule MyThing do
use Spark.Dsl,
single_extension_kinds: [:data_layer],
many_extension_kinds: [
:authorizers,
:notifiers
],
default_extensions: [
extensions: [MyThing.Dsl]
],
opt_schema: [
validate_api_inclusion?: [
type: :boolean,
default: true
]
]
end defmodule AnotherApp do
use MyThing
...
end with the current meta-DSL in #48: defmodule MyThing.Dsl do
defmodule Dsl do
use Spark
entity ...
section ...
end
sections = Spark.load(Dsl)
use Spark.Dsl.Extension, sections: sections
end
defmodule MyThing do
use Spark.Dsl, default_extensions: [extensions: MyThing.Dsl]
end defmodule AnotherApp do
use MyThing
...
end This version of the API works with the constraints of how the macros currently work, but we could have: defmodule MyThing do
use Spark
entity ...
section ...
sections [:my_section]
transformers [...]
option_schema do
...
end
end defmodule AnotherApp do
use MyThing
...
end This seems like a better use of the Thus, I propose moving my current work to defmodule MyThing.Dsl do
defmodule Dsl do
use Spark.SectionDsl
end
sections = Spark.SectionDsl.load(Dsl)
use Spark.Dsl.Extension, sections: sections
end |
Really its just a matter of it being championed, completed and tested. I’d love for it to happen but I myself likely won’t be able to do it. |
Is your feature request related to a problem? Please describe.
Now, to create a DSL with Spark, you need to manually manipulate
Entity
andSection
structs.Describe the solution you'd like
I propose creating a DSL in Spark to ease the creation of Entities and Sections.
This is:
Express the feature either with a change to resource syntax, or with a change to the resource interface
The highest-level syntax I propose would look like this:
Which replaces/augments the current syntax:
Additional context
Thanks to @jimsynz for the idea!
I will comment on this issue with ideas for the DSL for specific fields so that we can discuss specifics as they arise.
Work remaining
Field
entityField
entityField
entityThe text was updated successfully, but these errors were encountered: