-
-
Notifications
You must be signed in to change notification settings - Fork 161
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
Remove needs from HTMLPage module #1480
Remove needs from HTMLPage module #1480
Conversation
Hmm... the introduction of the need for an I also wonder what this would look like for someone like me, where I just want my context available in every page/component without repeating the I kind of have the same concern with having to pass |
To me, the price of typing 20 characters is pretty low compared to the confusion of why we have a magic DSL in Lucky |
Have we had lots of folks that have been confused by If it was 20 characters in the app as a whole I'd agree, but if you have 200 pages that need the context, you're repeating yourself for all 200 pages without a clear way to DRY up the code. I also think Rails is evidence of folks not minding (somewhat confusing) magic if it means they end up with less boilerplate/thought around stuff that they'd expect the framework to pass around for you. |
I think I actually prefer the I have two layouts (Page base classes) in my app: one for logged in users and one for not-logged-in users. Being able to simply put As an application grows in complexity, this kind of encapsulation is necessary. The job of an action isn't to know the entire application it lives in and be able to re-create everything needed for it, it's simply to build the objects that it's page cares about. That's also why frameworks inevitably end up adding an instrumentation framework where events can be listened to and acted upon without sprinkling code in every page / query / action. People get really frustrated by DSLs because it can be difficult to track down where the code actually gets run... but an eloquent DSL is a powerful way to express solutions to a problem and reduces cognitive burden -- that's what a programming language is in the first place. Ruby has many fantastic examples of quality DSLs and Lucky does too. |
This is not quite correct. We added Overall, I'm torn on this concept... I can see some things would be really good, but also some I'm not sure on.
I've never really been a huge fan of all the DSL stuff, but I also get it. For me, DSL should be salt & pepper on top. Just small sprinklings to enhance where needed. I do think it would be nice to be a bit more of just "Raw Crystal". It makes it easier when someone says "How do I do X?", and we say "It's all just Crystal.. make a class, write a method, and call it!". AuthenticatedContext.new(wrapped: context, current_user: current_user) I love this idea. I'm all for the pattern of passing a single context object instead of multiple arguments. Also not attaching directly to class Secrets::ShowPage < MainLayout
getter context : AuthenticatedContext
getter secret : Secret
def initialize(@context, @secret)
# context.request
# context.current_user
end
end This setup worries me though. Aside from the extra typing across tons of pages, I feel like it could become problematic from a type-safe standpoint. Being able to do this should be looked at more as an escape hatch rather than the recommended path. Maybe we can just look at creating a base abstract Context class. |
To be clear, this is no less type safe than the I think I should point out, I don't like the You'll find if you look at any other web framework that is statically typed, this is the kind of solution they go with. This is also not to say that we can't have the needs stuff, I guess. The only thing I would want, though, is that it's not in the |
@matthewmcgarvey when talking about DSLs and crystal primitives, I wonder if you're drawing a distinction I'm not seeing between |
@robacarp I'm not against DSLs, but if a DSL is restrictive and missing functionality it makes sense that there should be a way to go around it. In the case of Then there's issues and missing functionality. The most obvious one we've already mentioned above is that components don't have the same infrastructure code that pages do. But there's others, like you can't So I'm wondering, is the DSL covering up a genuinely complicated or verbose alternative? That's what I'm attempting to experiment with here. This is just one idea of how to solve it without a DSL. I think there's more things to consider with this context idea but I'm focusing on the needs thing right now. One thing I'd like to come out of this little experiment is that there is not an "escape hatch" from the needs stuff right now. |
O ya, the other point:
|
I've been thinking about this. I may be down for going this route, but I don't see this as necessary to get us to 1.0. The original issue we were trying to solve was passing a ton of things in to components. Nothing is actually broken or missing, it's just more of "Can we make this better?". Right now I'd like to focus on changes that are actually broken / missing that can get us to 1.0 and revisit this after. |
I’m pretty in favor of keeping needs. I’m not a fan of writing the initialized and making different contexts for things. I don’t have a ton of time to go in depth but my main reasons are that:
However, the error messages are a pain. That’s for sure. Maybe there is a way to improve those in the macro? I do think ignoring unused variables is not as safe, but I think it is far more convenient and user friendly. We didn’t do that at first and people had a REALLY hard time with it. In an ideal world id love to not do it but it seems most applications have enough edge cases in their logic that the trade off seems worth it. I do think we should probably remove allowing unused arguments from components though. I think I’m those cases it can be a pain |
@matthewmcgarvey thanks for the explanation. I thought that might be where you were headed with that, and I agree with you in that principle. What I like to see is the best of both worlds. I want a low-barrier to entry of boilerplate code so I can tear through the busywork of standing up an application CRUD as fast as possible. In any app there are 6 magic endpoints which do the "important" work and there are 350 endpoints that just need to be the same copy-pasta CRUD. In the in the rare case where it's insufficient I'd like to be able to override it, but in the landslide majority case I want it to be terse, readable, and easy to learn. Maybe needs as it is currently implemented doesn't serve that last case -- but as yet I haven't found a (ahem!) need that it doesn't suffice. |
The intention behind "needs" was to allow for passing data down into html pages without having to do it in every place we render each page. Things like
current_user
would be a pain to add everytime you render a page and there would be a ton of places to update if you need to add something or refactor in another way. Soneeds
was added to abstract away the way we "new up" an object whileexpose
was added to include data into this "newing up" process.Behind the scenes, expose is adding that reference to a global list of exposed data points and the
html
macro is newing up the page and passing in all exposed data points while the page takes what it needs from all the things passed in and ignores the rest.That all kind of sounds nice, but that's a lot of infrastructure/code and custom DSL to solve this issue.
There's also problems it causes:
html
macro is different than all our other response methods (json
,plain_text
, etc.) and you can't set an http statusAll this makes me want to rethink how we do this for pages. The question I have mulled over for quite a while is, "Can we accomplish something that solves 70-90% of the problem without a DSL?"
I was commenting on an issue and said that someone could probably just monkey-patch their state that they need everywhere onto
HTTP::Server::Context
since it seems like that's what it's for and it hit me. The context object is absolutely where state we want passed everywhere could go. To be clear maybe notHTTP::Server::Context
exactly but why not aMyApp::CustomHTTPContextWrapper
that wraps the http context and adds other accessors for things like current_user. It doesn't even have to be used in the action level, it could just be made to pass into a page and there could be a method used so that you can hide all the data that's getting wrapped up.With this implementation we almost get the same level of simplicity with none of the macro DSLs. Plus, if you don't use anything that needs the context, you don't have to pass it in. The url helpers require it, but if you never use the helpers, it will never complain about the context missing since it accesses the context like
@context
and without specifying the type.