[Lift] A Menu DSL

35 views
Skip to first unread message

David Pollak

unread,
Apr 23, 2010, 12:58:34 PM4/23/10
to liftweb
Folks,

Creating Menu items in Lift is generally a more verbose process than it needs to be.  We opened the following ticket: https://liftweb.assembla.com/spaces/liftweb/tickets/458-nicer-dsl-for-creating-menus to address the issue.

I have created a simple DSL for creating Menu(Loc(....)) pairs.  The existing code looks something like:
    Menu(Loc("home", List("index"), "Home")) ::
  Menu(Loc("Interactive", List("interactive"), "Interactive Stuff"),
       Menu(Loc("chat", List("chat"), "Comet Chat", Unless(() => Props.inGAE, "Disabled for GAE"))),
       Menu(Loc("longtime", List("longtime"), "Updater", Unless(() => Props.inGAE, "Disabled for GAE"))),
       Menu(Loc("ajax", List("ajax"), "AJAX Samples")),
       Menu(Loc("ajax form", List("ajax-form"), "AJAX Form")),
       Menu(Loc("js confirm", List("rhodeisland"), "Modal Dialog")),
       Menu(Loc("json", List("json"), "JSON Messaging")),
       Menu(Loc("stateless_json", List("stateless_json"), "Stateless JSON Messaging")),
       Menu(Loc("json_more", List("json_more"), "More JSON")),
       Menu(Loc("form_ajax", List("form_ajax"), "Ajax and Forms"))

The first param in Loc() is the name of the name of the Loc and must be unique.  It's used to look up Locs (e.g., <lift:menu.item name="Home"/>).  The next parameter is the URL of the page.  The third parameter is the display name (it is either call-by-name or an explicit function).  Locs can have additional Loc.LocParam parameters (e.g., If, Unless, Hidden, etc.).  A Menu have have submenu items.  They're a vararg sequence at the end of the Menu().

I've changed things up so you define a Menu as:

    Menu("Home") / "index",
    Menu("Interactive Stuff") / "interactive" submenus(
      Menu("Comet Chat") / "chat" >> noGAE,
      Menu("Ajax Samples") / "interactive" / "ajax",
      Menu("Ajax Form") / "interactive" / "ajax-form",
      Menu("Modal Dialog") / "interactive" / "rhodeisland",
      Menu("JSON Messaging") / "interactive" / "json",
      Menu("Stateless JSON Messaging") / "stateless_json",
      Menu("More JSON") / "json_more",
      Menu("Ajax and Forms") / "form_ajax") ,

You define a Menu item with either the name -> display text pair or just the display text (a random name will be generated).  Then you define the path to the page using "/" characters as separators.  Params are specified with >> and if the menu has submenus, they're defined with submenu.

The DSL deals with the most common menu creation tasks (it deals with Loc[Unit], but not Loc[T], etc.)

Before I put the code up on review board, I figured I'd toss the DSL out to the community to get feedback.  What do you folks think?

Thanks,

David

--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Naftoli Gugenheim

unread,
Apr 23, 2010, 1:09:25 PM4/23/10
to liftweb
Looks beautiful!
Could 'submenus' be renamed to 'apply' so you can leave it out? E.g.,

Menu("Interactive Stuff") / "interactive" (
      Menu("Comet Chat") / "chat" >> noGAE,
      Menu("Ajax Samples") / "interactive" / "ajax"
)
?

Naftoli Gugenheim

unread,
Apr 23, 2010, 1:10:05 PM4/23/10
to liftweb
Also, can the old syntax be intermingled with the DSL?

Mads Hartmann Jensen

unread,
Apr 23, 2010, 2:03:54 PM4/23/10
to lif...@googlegroups.com, liftweb
David, 

I don't have much to say besides I love it! :) Looking forward to incorporate it in my projects :)

Mads  

Sent from my iPhone

TylerWeir

unread,
Apr 23, 2010, 2:52:36 PM4/23/10
to Lift
On Apr 23, 1:09 pm, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> Looks beautiful!
> Could 'submenus' be renamed to 'apply' so you can leave it out? E.g.,

I like the explicit nature of "submenus" so I'd vote for keeping it as
such.
Not sure that brevity in this case helps.


>
> Menu("Interactive Stuff") / "interactive" (
>       Menu("Comet Chat") / "chat" >> noGAE,
>       Menu("Ajax Samples") / "interactive" / "ajax"
> )
> ?
>
> On Fri, Apr 23, 2010 at 12:58 PM, David Pollak <
>
>
>
> feeder.of.the.be...@gmail.com> wrote:
> > Folks,
>
> > Creating Menu items in Lift is generally a more verbose process than it
> > needs to be.  We opened the following ticket:
> >https://liftweb.assembla.com/spaces/liftweb/tickets/458-nicer-dsl-for...to
> > Beginning Scalahttp://www.apress.com/book/view/1430219890
> > Follow me:http://twitter.com/dpp
> > Surf the harmonics
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "Lift" group.
> > To post to this group, send email to lif...@googlegroups.com.
> > To unsubscribe from this group, send email to
> > liftweb+u...@googlegroups.com<liftweb%2Bunsu...@googlegroups.com>
> > .
> > For more options, visit this group at
> >http://groups.google.com/group/liftweb?hl=en.
>
> --
> You received this message because you are subscribed to the Google Groups "Lift" group.
> To post to this group, send email to lif...@googlegroups.com.
> To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
> For more options, visit this group athttp://groups.google.com/group/liftweb?hl=en.

harryh

unread,
Apr 23, 2010, 3:07:35 PM4/23/10
to Lift
How much is the menu generation functionality (<lift:menu>) in Lift
actually used out in the world? It might be worth letting sites that
don't want/need it to be able to avoid using it completely (by which I
mean, not having to give every page on the site a human readable menu
label).

-harryh

Marius

unread,
Apr 23, 2010, 3:21:59 PM4/23/10
to Lift
I'm wondering about internationalization. How would that be solved
with this DSL if one has a site in more than one language?

On Apr 23, 7:58 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> Folks,
>
> Creating Menu items in Lift is generally a more verbose process than it
> needs to be.  We opened the following ticket:https://liftweb.assembla.com/spaces/liftweb/tickets/458-nicer-dsl-for...
> Beginning Scalahttp://www.apress.com/book/view/1430219890

Lukasz Kuczera

unread,
Apr 23, 2010, 4:27:38 PM4/23/10
to Lift
That was one of things I didn't like about Lift, especially if you are
new it looks ubercomplex. New syntax at least looks nice and name
generation is good too (AFAIR it was made optional recently).
Its wonderful to see improvements in real time :)
How do you provide multiple params ?

On Apr 23, 5:58 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> Folks,
>
> Creating Menu items in Lift is generally a more verbose process than it
> needs to be.  We opened the following ticket:https://liftweb.assembla.com/spaces/liftweb/tickets/458-nicer-dsl-for...
> Beginning Scalahttp://www.apress.com/book/view/1430219890

David Pollak

unread,
Apr 23, 2010, 5:18:19 PM4/23/10
to lif...@googlegroups.com
On Fri, Apr 23, 2010 at 12:07 PM, harryh <har...@gmail.com> wrote:
How much is the menu generation functionality (<lift:menu>) in Lift
actually used out in the world?  It might be worth letting sites that
don't want/need it to be able to avoid using it completely (by which I
mean, not having to give every page on the site a human readable menu
label).

Yeah... the new stuff no longer requires the human readable label.
 

-harryh

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.


--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

David Pollak

unread,
Apr 23, 2010, 5:22:24 PM4/23/10
to lif...@googlegroups.com
On Fri, Apr 23, 2010 at 12:21 PM, Marius <marius...@gmail.com> wrote:
I'm wondering about internationalization. How would that be solved
with this DSL if one has a site in more than one language?

The Link Text is still a LinkText with the following implicit conversions:


  implicit def nodeSeqToLinkText[T](in: => NodeSeq): LinkText[T] = LinkText[T](T => in)
  implicit def strToLinkText[T](in: => String): LinkText[T] = LinkText(T => Text(in))


That means if you do Menu(S ?? "Catfood"), then the menu link will be properly localized.

Also, the signature is:

object Menu {
  def apply(linkText: LinkText[Unit]): ....
}

So, you can put anything that will calculate LinkText into the new Menu DSL.
 



--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890

harryh

unread,
Apr 23, 2010, 5:22:46 PM4/23/10
to Lift
> Yeah... the new stuff no longer requires the human readable label.

I'm referring to the "Home" bit from the code you posted:

> Menu("Home") / "index",

At least some sites don't care about that.

David Pollak

unread,
Apr 23, 2010, 5:23:13 PM4/23/10
to lif...@googlegroups.com
On Fri, Apr 23, 2010 at 1:27 PM, Lukasz Kuczera <kuk...@gmail.com> wrote:
That was one of things I didn't like about Lift, especially if you are
new it looks ubercomplex. New syntax at least looks nice and name
generation is good too (AFAIR it was made optional recently).
Its wonderful to see improvements in real time :)
How do you provide multiple params ?

Menu("Dog") / "woof" >> Hidden >> If(...) >> OtherParam


 



--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890

David Pollak

unread,
Apr 23, 2010, 5:25:50 PM4/23/10
to lif...@googlegroups.com
On Fri, Apr 23, 2010 at 2:22 PM, harryh <har...@gmail.com> wrote:
> Yeah... the new stuff no longer requires the human readable label.

I'm referring to the "Home" bit from the code you posted:

> Menu("Home") / "index",

So, you'd opt for Menu() / "index" and use SiteMap exclusively for access control?
 

At least some sites don't care about that.

-harryh

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.


--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

Heiko Seeberger

unread,
Apr 24, 2010, 2:04:36 AM4/24/10
to lif...@googlegroups.com
I did something similar in two of my projects and I like it a lot!

Could we please add a third constructor to Menu taking a (implicit) function List[String] => String? This would calculate/return the name based on the path.

E.g. 
Menu(nameCalc) / "login" / "signup"

Or with an implicit even shorter
Menu() / "login" / "signup"

Heiko
--
Heiko Seeberger

Company: weiglewilczek.com
Blog: heikoseeberger.name
Follow me: twitter.com/hseeberger
OSGi on Scala: scalamodules.org
Lift, the simply functional web framework: liftweb.net
Stambecco, highly scalable computing: stambecco.org

Timothy Perrett

unread,
Apr 24, 2010, 4:21:55 AM4/24/10
to Lift
I agree Ty.... its more natural to declare its a submen; more n00b
compatible for grokking!

On Apr 23, 7:52 pm, TylerWeir <tyler.w...@gmail.com> wrote:
> On Apr 23, 1:09 pm, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
>
> > Looks beautiful!
> > Could 'submenus' be renamed to 'apply' so you can leave it out? E.g.,
>
> I like the explicit nature of "submenus" so I'd vote for keeping it as
> such.
> Not sure that brevity in this case helps.
>
>
>
>
>
>
>
> > Menu("Interactive Stuff") / "interactive" (
> >       Menu("Comet Chat") / "chat" >> noGAE,
> >       Menu("Ajax Samples") / "interactive" / "ajax"
> > )
> > ?
>
> > On Fri, Apr 23, 2010 at 12:58 PM, David Pollak <
>
> > feeder.of.the.be...@gmail.com> wrote:
> > > Folks,
>
> > > Creating Menu items in Lift is generally a more verbose process than it
> > > needs to be.  We opened the following ticket:
> > >https://liftweb.assembla.com/spaces/liftweb/tickets/458-nicer-dsl-for...

Timothy Perrett

unread,
Apr 24, 2010, 4:33:32 AM4/24/10
to Lift
I like this a lot more than what we have currently for sure! Defo a
step in the right direction :-)

Using the Menu case class feels clunky - why dont we distil that down
to a symbol (seeing as the rest of the dsl uses symbology):

<< "home" / "some" / "path" >> noGae

Something like that would be good IMHO

Cheers, Tim

On Apr 23, 5:58 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> Folks,
>
> Creating Menu items in Lift is generally a more verbose process than it
> needs to be.  We opened the following ticket:https://liftweb.assembla.com/spaces/liftweb/tickets/458-nicer-dsl-for...
> Beginning Scalahttp://www.apress.com/book/view/1430219890

Timothy Perrett

unread,
Apr 24, 2010, 4:38:55 AM4/24/10
to Lift
Dont you guys use menu at foursquare?

I think there is a lot of value to be had in SiteMap and associated
Menu snippet - its an unusual thing to people coming from other
frameworks but once they get used to it it can be great.

Interested to know what you dont like about it :-)

Cheers, Tim

David Pollak

unread,
Apr 24, 2010, 9:17:02 AM4/24/10
to lif...@googlegroups.com
On Sat, Apr 24, 2010 at 1:33 AM, Timothy Perrett <tim...@getintheloop.eu> wrote:
I like this a lot more than what we have currently for sure! Defo a
step in the right direction :-)

Using the Menu case class feels clunky

It's not a case class, it's an apply method on Menu.
 
- why dont we distil that down
to a symbol (seeing as the rest of the dsl uses symbology):

<< "home" / "some" / "path" >> noGae


This is not possible within Scala's syntax.  Also, Menu mean something to people, especially newbies.  We don't want Lift turning into ScalaZ syntax-wise.  Yeah, it's great for people who know it, but the collection of symbols that only have a meaning once you've spent 6 months working with the system makes the learning curve very difficult. (Note I think there are a ton of good concepts in ScalaZ, but the mental cost of getting to those concepts is non-trivial.)

Using the "/" as a path separator makes sense because that's what it is.  Using >> is debatable but once your mind has the context that you're creating a menu, then you can look at what comes after the >> and say, "oh, those are the rules for this menu".  I've added the rule method so you can chain params:

Menu("Home") / "index" rule Hidden submenus(...)



--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890

Timothy Perrett

unread,
Apr 24, 2010, 3:29:23 PM4/24/10
to lif...@googlegroups.com
Valid concerns for sure David.

Perhaps, if you want to keep the Menu syntax with the apply method, then perhaps consider providing an alias method to apply that uses some symbol? On the same turn of the coin that Menu("XX") is more explanatory for n00bs, its also more verbose for experienced jedis ;-)

Cheers, Tim

On 24 Apr 2010, at 14:17, David Pollak wrote:

> This is not possible within Scala's syntax. Also, Menu mean something to people, especially newbies. We don't want Lift turning into ScalaZ syntax-wise. Yeah, it's great for people who know it, but the collection of symbols that only have a meaning once you've spent 6 months working with the system makes the learning curve very difficult. (Note I think there are a ton of good concepts in ScalaZ, but the mental cost of getting to those concepts is non-trivial.)
>
> Using the "/" as a path separator makes sense because that's what it is. Using >> is debatable but once your mind has the context that you're creating a menu, then you can look at what comes after the >> and say, "oh, those are the rules for this menu". I've added the rule method so you can chain params:
>
> Menu("Home") / "index" rule Hidden submenus(...)

David Pollak

unread,
Apr 24, 2010, 6:50:43 PM4/24/10
to lif...@googlegroups.com
Tim,

On Sat, Apr 24, 2010 at 12:29 PM, Timothy Perrett <tim...@getintheloop.eu> wrote:
Valid concerns for sure David.

I'm really glad you find "This is not possible within Scala's syntax. " to be a valid concern.  Some people might ignore the technical infeasibility of something and continue to argue for their point of view.

But you know, I may be wrong about using << as the lead because it's precedence is lower than /.  In that case, please write code that actually demonstrates the concept.

More broadly, nothing in this particular feature (or the REST stuff) is core to Lift or requires any changes to Lift.  In these cases and in future instances, please write the half dozen lines of code to test out a concept and include them with a post.  It's all stuff that could be done in the REPL or in a single, small file run in the Scala interpreter.
 

Perhaps, if you want to keep the Menu syntax with the apply method, then perhaps consider providing an alias method to apply that uses some symbol? On the same turn of the coin that Menu("XX") is more explanatory for n00bs, its also more verbose for experienced jedis ;-)

So, even if it were possible, I reject the idea of using symbols in this case.

While there is some value in saving a few characters because those characters add up.  But they usually add up when it comes to saving the characters in terms of composing stuff rather than saving 3 or 4 characters on a line (and in this case, you could save the same number of characters by changing Menu to M and if this issue is so important, you should be lobbying EPFL to add the ternary operator to Scala rather than if/else).

There's a perennial issue that comes up in Scala-land with (0 /: list)(_ + _) vs. list.foldLeft(0)(_ + _).  The former is a construct that has fewer characters and really gets under the skin of almost everyone (including me).  It's another symbol that has no context.  Yeah, it's fewer characters and shows off the Jedi-ness of the author, but it lends to the argument that Scala's got the line-noise quotient of Perl.  That's not something that I want to perpetuate.  In both cases, it's literally the savings of a couple of characters.  There's no additional way to compose and this is in contrast to adding operators to BigInt (vs. Java's named methods), there's no readability benefit to the savings of the few characters.  See Viktor's comments about something where having symbols makes sense: http://blog.gmane.org/gmane.comp.lang.scala.debate/month=20080201/page=11

We saw this issue come up in the context of the ?~ method on Box.  I'm the only person around that uses that method (and it has a mnemonic -- if not -- ?~)... every instance of third-party code I've seen uses failMsg.  And the comment about the ?~ and ~> characters in the list post about Box was pretty much "how is anyone expected to remember those characters?"

So your proposal is something that can't be done and even if it could be done (e.g., we change the symbol to ~>>), it would be a bad idea.  Further, if you really, really want it, it's something you can do in your own libraries which you can publish as helper tools.

David
 

Cheers, Tim

On 24 Apr 2010, at 14:17, David Pollak wrote:

> This is not possible within Scala's syntax.  Also, Menu mean something to people, especially newbies.  We don't want Lift turning into ScalaZ syntax-wise.  Yeah, it's great for people who know it, but the collection of symbols that only have a meaning once you've spent 6 months working with the system makes the learning curve very difficult. (Note I think there are a ton of good concepts in ScalaZ, but the mental cost of getting to those concepts is non-trivial.)
>
> Using the "/" as a path separator makes sense because that's what it is.  Using >> is debatable but once your mind has the context that you're creating a menu, then you can look at what comes after the >> and say, "oh, those are the rules for this menu".  I've added the rule method so you can chain params:
>
> Menu("Home") / "index" rule Hidden submenus(...)

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.


--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

Timothy Perrett

unread,
Apr 24, 2010, 7:26:10 PM4/24/10
to lif...@googlegroups.com
David, no problem, I get you dont want symbols in Lift. Although, I too use ?~ and prefer it over failMsg... I think its something people grow into when they don't need the comfort blanket of descriptive method names. Its not even a matter of fewer characters... thats not the driver here: I just prefer symbols :-)

Using << was just a suggestion / talking point. You did after all ask for thoughts and feedback...

Hannes

unread,
Apr 26, 2010, 2:45:02 AM4/26/10
to lif...@googlegroups.com
Hi Dave,

Thanks a lot, that will make things much more easier to use!

I think this is probably something that makes the use of the Lift Framework in general more complicated: Method invocation sometimes need's a lot of knowledge about the implementation of that specific method. This is a really good example, nobody really need's to know that the first parameter is a "Loc" or whatever to create a menu item - this can just be "abstracted away" like you did......(don't know a better phrase, yet)

I would really like to do some improvements like that, but honestly I'm not having the time at the moment - Lift is just a side project (but a really interesting one).

Do you think you (or we) can do the same thing with CRUDify?

I would like to get started with CRUDify, but I think I need some hints, where.

thanks.

Jeppe Nejsum Madsen

unread,
Apr 26, 2010, 4:25:17 AM4/26/10
to lif...@googlegroups.com
On Fri, Apr 23, 2010 at 11:22 PM, David Pollak
<feeder.of...@gmail.com> wrote:
>
>
> On Fri, Apr 23, 2010 at 12:21 PM, Marius <marius...@gmail.com> wrote:
>>
>> I'm wondering about internationalization. How would that be solved
>> with this DSL if one has a site in more than one language?
>
> The Link Text is still a LinkText with the following implicit conversions:
>
>
>   implicit def nodeSeqToLinkText[T](in: => NodeSeq): LinkText[T] =
> LinkText[T](T => in)
>   implicit def strToLinkText[T](in: => String): LinkText[T] = LinkText(T =>
> Text(in))
>
>
> That means if you do Menu(S ?? "Catfood"), then the menu link will be
> properly localized.
>
> Also, the signature is:
>
> object Menu {
>   def apply(linkText: LinkText[Unit]): ....
> }
>
> So, you can put anything that will calculate LinkText into the new Menu DSL.

I just ran into this issue today. I usually give menu entries a name
and use the same name as the key for the 18n string. I can't see if
there's an easy way (well, easier than recreating the Menu object :-)
to combine this so that

Menu("catfood")

will be the same as Menu("catfood", S ? " catfood"). Maybe with some
extra implict-fu, the meaning of apply: String => PreMenu could be
customized to different use cases?

/Jeppe

David Pollak

unread,
Apr 26, 2010, 4:00:00 PM4/26/10
to lif...@googlegroups.com

How about IMenu("catfood") (or some other name)? That'll make it much easier to do a mapping to the Menu() stuff.
 

/Jeppe

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.


--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

Jeppe Nejsum Madsen

unread,
Apr 26, 2010, 4:49:24 PM4/26/10
to lif...@googlegroups.com
Ahh yes, very nice & simple.
Reply all
Reply to author
Forward
0 new messages