-
-
Notifications
You must be signed in to change notification settings - Fork 19
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
Introduce customizable scope objects #90
Conversation
9513050
to
4edc05c
Compare
Pinging @solnic @jodosha and @AlfonsoUceda to take a look at this one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@timriley I really like the idea.
The most important bit is to communicate properly the following lines:
I'm very excited to see the addition of scopes, because it means we now have an appropriate place for every kind of view behaviour, whether it's global (context!), specific to a particular value (parts!) or specific to a particular controller or template (scopes!).
That means to clarify the three levels (global, local, value) of intervention for a user, and the corresponding tools (context, scope, and parts).
Speaking of which, what's the precedence of these tools? 1. Part 2. Scope 3. Global?
@jodosha I'm really glad to hear like the approach. I agree 100% that a big part of helping people get on board with dry-view is clearly articulating each of these facilities and the purpose for each (along with some example cases, perhaps, to make things clearer). I'll be working on user docs once I've got through most of the 1.0 issues. The order of precedence when calling a method within a template is:
|
@solnic @flash-gordon @GustavoCaso If you have a chance, I'd love your feedback on the refactoring I've just pushed in 91c0b3d. The background for this is that now we're adding support for custom "scope" classes, we've added a So to tidy up, I've introduced a However, the reason why I'd like some feedback here is that the I feel this approach is OK given the interrelatedness of these objects, and it's certainly helped tidy up our internals here. But if you think this kind of arrangement is a red flag, I'd love to hear your thoughts on how else we could approach it. |
@timriley I have check the refactor you mentioned in 91c0b3d I see no issue with it, it actually pushes the logic one level down leaving the Id true that def for_rendering(rendering)
return self if rendering == self.rendering
self.class.new(namespace: namespace, rendering: rendering)
end is not a very usual pattern in ruby, but as you say we need the rending because One question: While we are refactoring I noticed that we have a bunch of private class methods in # @api private
def self.rendering(format: config.default_format, context: config.default_context)
Rendering.prepare(renderer(format), config, context)
end
# @api private
def self.renderer(format)
renderers.fetch(format) {
renderers[format] = Renderer.new(paths, format: format, **config.renderer_options)
}
end
# @api private
def self.renderers
@renderers ||= {}
end
# @api private
def self.exposures
@exposures ||= Exposures.new
end Is there something stopping us from using |
Scopes provide a custom rendering context for a specific template or partial
This provides a unified, coherent API for rendering facilities (such as rendering templates or partials, as well as creating scopes and parts), and makes it easy to pass these interrelated objects around in one go, rather than having to manage them all individually.
This will be helpful when we want to prepare a standalone rendering object for testing purposes
c110e96
to
a5c1b6a
Compare
@GustavoCaso Thanks for taking a look. I actually have tentative plans to make |
Late to the party, but I'd like to add a comment. I'm using a custom scope with a method which just curries another method defined in the context. The important thing here is that I'm not calling the context method, I'm just referencing it. So, in order to reference it I have to access the context object, where it is defined: _rendering.context.method(:my_context_method).curry.(:one_arg) Using I know it is not a very common scenario, but still with a functional approach it makes a lot of sense having a generic method in the context specialized in custom scopes through currying. WDYT @timriley ? |
Of course, I could do it myself through a PR. |
This PR introduces a new, user-customisable concept to dry-view: scopes.
Until now, dry-view has given the users the following places to provide data and behaviour to the view:
This has been enough to cover many use cases, but it still left a gap: what if you wanted to provide some extra behaviour to particular templates, e.g. those within a single view controller rendering only, or even just to specific partials?
For this, we now have scopes. Scopes are just that - the rendering environment for a template. They consist of
locals
, thecontext
, as well as the other internal objects used for rendering, etc.You can create scopes from within templates as well as parts. When creating a scope, you pass a name (optional), as well as a hash of locals.
Scopes are created via a
ScopeBuilder
, which like thePartBuilder
, can be configured to infer a scope class from its name, searching within a given namespace.A scope can also be assigned to a view controller config itself, which provides the scope for the controller's base template.
To demonstrate the usefulness, let's run through a couple of concrete examples.
Adding extra behavior to a base template
template:
== hello
ruby code:
Providing default values for partials
This also demonstrates how a scope can be rendered without an explicit partial name, if the scope's name matches the partial's name.
template:
== scope(:greeting).render
_greeting
partial:| Greeting: #{greeting}
ruby code:
Both of these examples, while obviously simplified here, are derived from real issues we had in using dry-view across multiple complex client projects.
I'm very excited to see the addition of scopes, because it means we now have an appropriate place for every kind of view behaviour, whether it's global (context!), specific to a particular value (parts!) or specific to a particular controller or template (scopes!).
I still have some tidying to do before this PR is ready to merge, but I wanted to write up some brief thoughts on how this all works to seek some feedback at a feature/behavioural-level. Would love to hear what you think.
TODO: