midas-framework / midas Goto Github PK
View Code? Open in Web Editor NEWA framework for Gleam, Midas makes shiny things.
Home Page: https://hex.pm/packages/midas
A framework for Gleam, Midas makes shiny things.
Home Page: https://hex.pm/packages/midas
Probably need an error type defined in the template project
I think there is value in always having a simple interface to the server which consists of a single request-> response function.
However there is a lot of boiler plate in writing API's so what would a DSL look like.
Match(
"users",
[
Get(fn() { "All users" }),
Param(
string([MinLength(14), MaxLength(14)]),
[
Get(fn(user_id) { "user 1" }),
Match("update", [Post(fn(user_id) { "update user 1" })]),
Match(
"posts",
[
Param(
integer([Max(999)]),
[
QueryParam(
"published",
boolean,
[
Get(fn(user_id: String) {fn(post_id: Int) { fn(published: Bool) { todo } } } ),
],
),
],
),
],
),
],
),
],
)
Key feature of this one is every new extraction, e.g. Param/QueryParam takes a list of next steps, so branching can be introduced at any point, this reduces duplication.
Choice(
[
Segment("users", Get(fn() { "All users" })),
Segment("users", Param(string([MinLength(14), MaxLength(14)], Get(fn(user_id) { "user 1" })))),
Segment("users", Param(string([MinLength(14), MaxLength(14)], Match("update", Post(fn(user_id) { "update user 1" }))))),
Segment("users", Param(string([MinLength(14), MaxLength(14)], Match("posts", Param(integer([Max(999)]), QueryParam("published", boolean, Get(fn(user_id: String) {fn(post_id: Int) { fn(published: Bool) { todo } } } ))))))),
],
)
Formatter makes mess of above but assuming you reuse user_id declaration and handlers are defined elsewhere, routing can be simplified
user_id = string([MinLength(14), MaxLength(14)]
post_id = integer([Max(999)])
get_user = fn(user_id) { "user 1" }
update_user = fn(user_id) { "update user 1" }
get_published_posts = fn(user_id: String) {fn(post_id: Int) { fn(published: Bool) { todo } } }
Choice(
[
Segment("users", Get(get_users)),
Segment("users", Param(user_id, Get(get_user)))),
Segment("users", Param(user_id, Match("update", Post(update_user))))),
Segment("users", Param(user_id, Match("posts", Param(post_id, QueryParam("published", boolean, Get(get_published_posts))))))),
],
)
Need to use an explicit Choice
type when more than one route is available.
Has more duplication but the behaviour of reach route is clearer.
curry2
curry3
etc.I think it's possible to have controller files.
//get_posts.gleam
fn handle(user_id: String, post_id: Int, published: Bool) {
todo
}
fn endpoint(){
Segment("users", Param(user_id, Match("posts", Param(post_id, QueryParam("published", boolean, Get(curry3(handle)))))))
}
// router.gleam
import my_app/get_posts
Choice[
get_posts.endpoint
]
In summary the DSL is a bunch of error composition
the second option (particularly if grouped by controller) might as well be the clear fn call approach.
fn get_published_posts(){
try tuple(user_id, post_id) = web.match(request, Segment("users", Param(uuid, Segment("posts", Param(int, Done(tuple2))))))
try published = web.query_param(request, "published", integer([Max(999)]))
}
Can have a Response(serializer: fn(x) -> r, Get(return x))
// Could serialize just OK value
The best thing to do is to practise a parsing API on a smaller section of the problem, e.g. form/query params, where all raw values are strings, entries come as a list not map, etc.
And then expand it to requests if working well
[
Required("blah", int),
Optional()
Many("foo", int, [])
]
There should be an answer as to how to do this, probably more than one.
I suspect this is more something that should be set up in a template project instead of requiring code in the library
Tagged as Gleam because we will need an os.cmd
function.
type Request(body) {
...
In stead of requiring the body to be a String, it could also be an iolist or steam. Doing this would allow applications more choices when dealing with large requests.
Jiffy makes use of NIF's
jsone is simpler, jsx handles streaming etc
Reload beam modules on file changes.
Many web applications can be categorized as API services behind a load balancer. The first version of Midas is optimised for them.
Probably useful
required for server rendered apps
non-goals:
Might not be mix tasks
r3:compile()
that should recompile source.--start-clean
to start no application and run task.rel start
rel eval "myapp:"
Use build rules to push a new version to docker hub everytime this repo has a tag.
Implement a library for CORS
It's only a valid content-length if correctly parses as an integer. Is there a way to save this fact in the request type.
Can be hardcoded as a first version.
Not sure how best to do this. Would be great at the Gleam level, rather than midas so errors in background tasks where handled as well as they are in request handlers.
Things I would like to have but that need to come from Gleam, if you would like to help with Midas helping on these core issues would be just as useful
gleam-lang/gleam#34
Fixing this would make choosing Gleam much more compelling in general. It is also important to understand how this will effect error handing when let Ok(value) = risky()
no longer works
Done 0.8.0 gleam-lang/gleam#474
A Request in particular is a large record, having to individually ignore every field is painful
Done stdlib API docs. API reference, language documentation is limited to guides currently.
error: Invalid capture
let start = server.start_link(_, _)
The function capture syntax can only be used with a single _ argument,
but this one uses 2. Rewrite this using the fn(a, b) { ... } syntax.
Requires #26 to be completed first
See this Elixir forum post for my explination of typed processes up to this point.
A process traps exits by providing the function that will handle the message.
Start with a process with the following message type
pub type Message {
Foo
Bar
OhDear(BarePid, Dynamic)
}
The Exit type is OhDear
to make clear its a type defined by the client, it would I expect normally be called exit in practice.
process.spawn_link(fn(receive) {
process.trap_exit(receive, fn(pid, reason) { OhDear(pid, reason) })
// continue ...
})
Signature of trap_exit function.
fn trap_exit(fn(Wait) -> m, fn(BarePid, Reason) -> m)
process.spawn_link(fn(receive, trap_exit) {
trap_exit(fn(pid, reason) { OhDear(pid, reason) })
// continue ...
})
process.spawn_link(fn(receive) {
// continue ...
}, Ok(fn(pid, reason) { Exit(pid, reason) }))
process.spawn_link(fn(receive) {
// continue ...
}, Error(Nil))
Very similar except the mapping function is passed when the monitor is created.
process.monitor(pid, fn(pid, reason) { MyDown(pid) })
I think one route for adopting midas for me is if I could start it from within a mix/phoenix project and have it receive all HTTP requests and pass any that are unrecognised on to Phoenix. I'm unfamiliar with the lower levels of the HTTP stack in all of these projects so I've no idea how possible this might be.
From my perspective it would be the lowest friction way of introducing midas to a project that I already have. I was once keen for this kind of solution for migrating from Django to Phoenix obviously they are different tech stacks and I couldn't figure out how to make Phoenix proxy unrecognised requests over to the Django process so I ended up splitting all traffic at the nginx layer which is just a bit more hassle.
Anyway, I wanted to share the thought here as it would open up possibilities for trying Gleam & Midas more easily in some of my projects. Perhaps there are other downsides though and a clean break would be preferable for the development of the ecosystem.
Does it seem possible?
There should a flexible pool of acceptors waiting for connects, atm it is just fixed to 100 servers in parallel
Somewhere we need to have the information on messages for response types, however it's extensible
response(OK)
response(Custom(422, "Something"))
or have functions that return status + reason phrase
response(ok())
response(tuple(422, "Something)
It should be helpful in letting users set a name of the project and choosing other options, like to include a db
Probably easiest to copy from an existing implementation like plug
Requires some work for templating to be built into the language
https://github.com/gleam-lang/gleam/issues/565
gleam-lang/gleam#516
In mix projects a workout can be to use EEx and conventions but not suitable for 1.0
Should have:
Inspiration can come from Raxx.Session which was designed to have as functional an interface as possible https://github.com/CrowdHailer/raxx/tree/master/extensions/raxx_session
Supervisors currently accept a spawn_link
function, this however relies on the client to do it's own spawn call, remembering to link. There is no way to determine if the process is linked to the supervisor. (I think). This feels like duplication, can the supervisor just take the run function and handling spawn. Doing this removes the opportunity to do anything before starting the process. I can't think of any examples where I use that time other than just create a wrapper.
Making this change would allow supervisors to specify that they will only run processes they expect to last forever.
e.g.
Child {
Permanent(fn(Receive(m)) -> Never)
Transient(fn(Receive(m)) -> r)
}
Add an example endpoint that parses input.
This will probably make use of https://github.com/rjdellecese/gleam_decode and ideally the approach would be reusable for multiple input sources. e.g. query strings, JSON, forms
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.