The problem is that I often have data that needs to a) be presented in a
very specific way, meaning I want to implement render-widget-body myself
and (more importantly) b) be dynamically modifiable, e.g. have actions
rendered, participate in flows, have an "Edit" button for in-place
editing, etc.
The second issue (b) in particular is something presentations can't
easily do, because views are singletons and presentations share state.
So, that got me thinking -- why not reimplement views, but not as
separate stateless entities, but as widgets, containing other
widgets. One can then put form processing and the UI DSL on top of
those.
That way we could probably get rid of presentations (which would likely
become mixins, or simply widget protocols for accessing and storing data
types). Views would become widgets that have a mapping to/from data
model objects and can process form data.
Anyone having similar thoughts?
--J.
I mostly use views for forms (because of the validation stuff)
and grids (because it's a cool way to build a table).
> The problem is that I often have data that needs to a) be presented in a
> very specific way, meaning I want to implement render-widget-body myself
> and (more importantly) b) be dynamically modifiable, e.g. have actions
> rendered, participate in flows, have an "Edit" button for in-place
> editing, etc.
I also have these special needs, but mostly in my graphics-intensive
game application.
Hardly any views there...
> The second issue (b) in particular is something presentations can't
> easily do, because views are singletons and presentations share state.
Yes, I hate this.
> So, that got me thinking -- why not reimplement views, but not as
> separate stateless entities, but as widgets, containing other
> widgets. One can then put form processing and the UI DSL on top of
> those.
>
> That way we could probably get rid of presentations (which would likely
> become mixins, or simply widget protocols for accessing and storing data
> types). Views would become widgets that have a mapping to/from data
> model objects and can process form data.
>
> Anyone having similar thoughts?
I've given the problem some thought and also the the overlap between
widgets and views. So far I think this might be a good approach.
Requirements for a new model:
1. It should be as compatible as possible with the old one.
2. It should be easy to override a single slot of a view.
3. Inheritance must be working and customizable. We should also try to
offload inheritance stuff to CLOS to get rid of the ugly machinery
currently employed.
This all just off the top of my head...
I disagree here -- I don't think we should worry about backwards
compatibility, because there is so little to be compatible with. After
all, we can always E-mail both users of weblocks and have them change
their apps, right? :-)
> 2. It should be easy to override a single slot of a view.
Do you mean after scaffolding?
The more I work on applications, the less I think of scaffolding. In
fact, I just found that I can remove all the scaffold declarations in my
application, because I had to override everything anyway.
This might be different for people that write apps in English, but in my
case I _never_ want to use my data model field name as a label.
I believe scaffolding is complex, difficult to get right, sucks in huge
amounts of developer effort, and provides little real-world utility
except for small demos in English.
> 3. Inheritance must be working and customizable. We should also try to
> offload inheritance stuff to CLOS to get rid of the ugly machinery
> currently employed.
Do you need view inheritance or view encapsulation?
While I can't find a good use case for inheritance, I have lots of cases
where I would like to reuse views inside other views.
--J.
I think what was meant here was that adapting your model classes to also
serve as views does not work. I fully agree.
--J.
> On Jan 6, 7:35 pm, Jan Rychter <j...@rychter.com> wrote:
>
>> I disagree here -- I don't think we should worry about backwards
>> compatibility, because there is so little to be compatible with. After
>> all, we can always E-mail both users of weblocks and have them change
>> their apps, right? :-)
>
> Don't take this too lightly. I have two important apps built on
> Weblocks,
> you, Yarek, nunb, Robin and Ian each have one...
>
> Backwards compatibility should not keep us from implementing cool/
> clean
> things, but let's try to achieve at least a little bit of it.
Ok. But I'd rather design cool things first, and then figure out whether
they can be backwards compatible, than the other way around. I do not
think weblocks is mature enough to worry about compatibility yet. And I
certainly do not think the current design is good enough to warrant
keeping it just for the sake of backwards compatibility.
[...]
>> The more I work on applications, the less I think of scaffolding. In
>> fact, I just found that I can remove all the scaffold declarations in my
>> application, because I had to override everything anyway.
>
> Scaffolding is nice to get something up quickly. Think agile.
>
>
>> This might be different for people that write apps in English, but in my
>> case I _never_ want to use my data model field name as a label.
>
> I don't consider that a real reason since we're working on i18n
> support
> anyway.
What I meant is that in order to specify view labels I need to list all
the fields anyway. So really all scaffolding can possibly buy me is a
guess that if a field is of :type string then I probably want an input
presentation. Even then I often want extra arguments.
And if I do use scaffolding I have to worry about hiding the fields I do
not want to see (id, join slots, etc.)
And even if scaffolding were smart enough to figure out my join slots
and present them as dropdowns or checkbox lists, I would still need
additional annotations to tell the views which field to display in these
presentations.
>> I believe scaffolding is complex, difficult to get right, sucks in huge
>> amounts of developer effort, and provides little real-world utility
>> except for small demos in English.
>
> We can neglect it until the end and then see how difficult it would
> be.
>
>
>> > 3. Inheritance must be working and customizable. We should also try to
>> > offload inheritance stuff to CLOS to get rid of the ugly machinery
>> > currently employed.
>>
>> Do you need view inheritance or view encapsulation?
>
> Do you mean composition? If yes:
>
> Composition is way more important than inheritance, but inheritance
> can be pretty useful, too.
Yes, I meant composition, as in one view inside another. And I was
curious, because I never actually encountered an inheritance use case,
except for inheriting from scaffold views.
--J.
I often need to display, say a paragraph of text with an "edit" link
that would switch the paragraph to an YUI Rich Text Editor. Another case
is many-to-many relations (think tags), if you only have a small number
of them you can get away with presenting them as a bunch of checkboxes,
but for larger numbers you need an autocompleting input or something
entirely different, with widget-like behavior.
> In my case, often the presentations (someone else called them
> decorations) are object or context specific -- so what I usually do is
> create my views at the point of use with defview-anon; modify the view-
> suffix/prefix or field-suffix/prefix functions as necessary (that is,
> they become closures with access to the object on which their behavior
> depends) and then use them.
That is a neat trick (defview-anon), I haven't thought of that. But it
shows that something is wrong if we need to jump through hoops.
[...]
Thanks to everyone for the very helpful comments. I think I have a good
idea of what is wrong with our current views and how to fix it. I'll try
to work on it after I finish the navigation-rewrite branch, as time
permits. As usual, I'll do lots of experimentation to see what works and
what doesn't.
--J.
Same here. I use views for quick-and-dirty admin interfaces, but as
Stephen rightly pointed out, this is usually not the way to go for
modern apps. So I end up writing widgets _and_ presentations/views for
many things, which is a duplication of effort.
Scaffolding is something I stopped using altogether. I can see how it
can be useful for quick "let's throw a prototype together in five
minutes" things, but in the longer term it doesn't buy me anything.
[...]
> In fact, I would like to request/recommend that we look at tagging a
> stable point in the tree as a kind of interim release once the nav
> stuff and a version of the view functionality settles down. It would
> give everyone a common place to fork from, provide some community
> consensus on the core functionality, and make folks like me feel
> better about upgrading and perhaps starting to contribute code again!
I am pretty sure Stephen will do this at some point, but in the
meantime, I found forking to be less painful than I thought it would
be. Modern VCSs make maintaining forks much easier than it used to be.
Thanks for your comments -- they confirm what I felt. I have an idea on
how to make the view system work, I'll try to work on it in a topic
branch soon.
--J.
We do have some semi-planned releases, everyone.
Please see http://weblocks.lighthouseapp.com/projects/18897-weblocks/milestones
0.8.5 and 0.9 will have to be exchanged because of the nav advances
(and the sheer number of tickets scheduled for 0.8.5), but apart from
that it seems sensible.
Add a new view system, i18n and manual and we're close to 1.0 and its
release candidates.
I was joking -- I'm sorry if that came out too harsh. I don't take this
lightly at all, but I still think the current user base is very small
and most of these people are bulding apps, not servicing something that
has been deployed. The response to the recent poll about deployed
weblocks apps attests to that. If you're still working on your
application, it isn't such a big problem to modify it to fit new
interfaces. There might be a day when we'll write a compatibility layer
for the 1000 or so deployed applications -- but that day is not today.
So, in my opinion, this is still a great time to do backwards
incompatible major redesigns if they are needed. It will be much worse
as we move on and these things will become much harder.
Also, I believe there is always place for major changes, in experimental
branches (such as my branches on github). I don't know about other
people, but I never get a good design unless I try at least twice [1].
And I don't think we should make design compromises just because
changes would break apps.
--J.
[1] Fred Brooks got it right.
I'd like to clarify my position on this: incompatible changes
as needed, but if a (partial) compability layer is reasonably
simple to provide* then it should come with the new changes.
It would also be good to provide short migration notes
to help ease the process.
* in CLOS it's often surprisingly easy to provide
compatibility methods
Here's something I was throwing together just before disappearing:
(in-package #:weblocks)
#|
A new design for scaffolding views
* Specify view elements with slot kwargs
* :weblocks- always stripped
* CLOS-specced kwargs are always removed, but..
* :reader bool :writer bool :accessor bool is stripped and used
to override scaffold default rules
* :weblocks-view (view-names ...) ...args-for-only-this-view...
* The :scaffold pseudo-view is default
Slava wrote about the problems with strictly using information about a
class from defclass and the MOP to determine scaffolds. We can't use
new slot definition classes to keep the extra information we want,
because stores commonly do that for us, and anyway we still want the
store classes to strictly reflect the storage information.
But *syntactically*, we would like to provide information for views
within the class definitions. Fortunately, stores tend to either
publish a metaclass to use or a form very similar to `defclass'. In
these cases we can preprocess the defclass or defclass-like forms
directly to extract view-related information.
Note that for basic scaffolds, wrapping store classes with
`weave-scaffold-views' is unnecessary. However, to access the extra
features it provides, it must be used.
|#
(wexport '(weave-scaffold-views))
(defconstant +amop-slot-definition-keywords+
(if (boundp '+amop-slot-definition-keywords+)
+amop-slot-definition-keywords+
'(:allocation :initform :reader :writer :accessor :initarg :type))
"Keyword arguments accepted by `standard-direct-slot-definition's
via `defclass' forms. They are ignored by `weave-scaffold-views' when
constructing scaffold views.")
(defun weave-scaffold-class+views (class-form)
"Helper for `weave-scaffold-views'."
(destructuring-bind (defclass-macro-name defclass-name (&rest defclass-supers)
(&rest defclass-slots)
&rest defclass-params) class-form
(let ((views (make-hash-table :test 'eq))
new-defclass-slots)
(loop for (slotname . slotargs) in defclass-slots
do (loop with new-slotargs = '()
for (argnm argval) on slotargs
do (cond ((and (find argnm '(:reader :writer :accessor))
(typep argval 'boolean))
#|TODO save accessibility and skip|#)
((find argnm +amop-slot-definition-keywords+)
#|TODO drop|#)
(t #|TODO save to view|#
(setf new-defclass-slots
(list* argval argnm new-slotargs))))))
(values `(,defclass-macro-name ,defclass-name ,defclass-supers
,(nreverse new-defclass-slots)
. ,defclass-params)
'())))
(defmacro weave-scaffold-views (&rest class-forms)
"Output the body, a `defclass'-like form, with parts removed as
described, followed by definitions of views for objects of
DEFCLASS-NAME."
`(progn ,@(mapcar (f_ (multiple-value-bind (class forms)
(weave-scaffold-class+views _)
`(prog1 ,class ,@forms)))
class-forms)))
;;; Anyway you get the idea.
--
Sorry but you say Nibiru is a Hoax? Doesnt Exist? So maybe The
Sumerian people doesnt exist also! --Anonymous by way of SkI