So i've been sitting on this for a while, and wanted to bring it on list to discuss further. LiftRules has become a very large class in Lift, one of the biggest in fact. I feel that as time moves on, we could do with a more componentized approach to configuration so that code navigation is easier and use of the configuration object is more intuitive.
I would like to propose splitting out everything in LiftRules in the following manner:
1. create a namespace for rules: net.liftweb.rules
2. create composable but critically sealed traits that include inner objects for configuration. For example:
sealed trait AJAX {
object AJAX {
...
}
}
there would of course be several different configuration elements and parts relating to session, http, ajax, comet and so forth. I was originally concerned that as some of the functions span contexts, it might be an issue, but whilst refactoring I actually didn't find that to be too much of an issue as things could be logically ordered. From a client API perspective, they would then have a more normalized API:
AJAX.path
rather than
LiftRules.ajaxPath
That is of course one example, but hopefully it illustrates what I am proposing. Moreover, I think it would also be good to normalize the API to use FactoryMaker. Whilst in an of itself, this could be a breaking change, testing has shown me that it is indeed possible to make a 100% backward compatible LiftRules object using delegation, so a large refactor like this could be introduced without impacting on API usage in the near term, whilst allowing us room to grow and continue to make lift flexible and extensible. From a long-term POV, if there was a concern around keeping LiftRules, it could be factored out into lift-legacy project or something where it could delegate to the main configuration object so that people who are slow to migrate could have an easier time.
Hopefully this is enough information to start a helpful discussion :-)
Cheers, Tim
> discuss further. LiftRules has become a very large class in Lift, one of
> the biggest in fact. I feel that as time moves on, we could do with a more
> componentized approach to configuration so that code navigation is easier
> and use of the configuration object is more intuitive.
Speaking as a relatively new Lift User I must say that I found dealing with
the giant LiftRules a bit unwieldy. If you don't know exactly what you are
looking for it is hard to find your way around. Splitting up LiftRules the way
you suggested would make it much easier for newcomers to find the necessary
configuration. Also, the current naming is not always consistent, most ajax
configurations are named ajaxFooBar, like ajaxRetryCount but then some do not
follow that convention like autoIncludeAjax. Because of this one always has to
go through the whole javadoc of LiftRules.
Still loads better than struts xml, though ;)
Agreed 100%
> I would like to propose splitting out everything in LiftRules in the following manner:
>
> 1. create a namespace for rules: net.liftweb.rules
> 2. create composable but critically sealed traits that include inner objects for configuration. For example:
>
> sealed trait AJAX {
> object AJAX {
> ...
> }
> }
>
I'm not sure I see clearly how this works/should be used.
I assume you have a trait so you mixin this trait where e.g. Ajax
rules are needed and thus don't rely on a global AJAX object?
Why should the trait be sealed?
/Jeppe
sealed trait Environment {
object Environment {
...
}
}
sealed trait HTTP { _: => Environment
object HTTP {
...
}
}
sealed trait AJAX { _: => Environment with HTTP
object AJAX {
...
}
}
object Application
extends Environment
with HTTP
with AJAX
...something like that at least. The thinking being that then the configuration is name-spaced within Applicaiton, so you'd have a boot file like:
class Boot {
def boot {
import Application._
// config goes here
}
}
Does that help clarify?
Cheers, Tim
Cheers, Tim
On 11 Oct 2010, at 11:33, Jeppe Nejsum Madsen wrote:
Somewhat, but how do you use the rules? Is Application Lift code or
User code? Something needs to be declared somewhere for the Lift code
to use :-)
/Jeppe
Cheers, Tim
class Boot {
def boot {
import net.liftweb.http.config.Application._
if (!DB.jndiJdbcConnAvailable_?) {
val vendor = new StandardDBVendor(...)
HTTP.unloadHooks.append(vendor.closeAllConnections_! _)
DB.defineConnectionManager(DefaultConnectionIdentifier, vendor)
}
HTTP.addToPackages("test")
Schemifier.schemify(true, Schemifier.infoF _, User)
def sitemap() = SiteMap(...)
Sitemap.setSiteMapFunc(sitemap)
// FIXME probably should be AJAX.start / AJAX.end
Comet.ajaxStart = Full(() => LiftRules.jsArtifacts.show("ajax-loader").cmd)
Comet.ajaxEnd = Full(() => LiftRules.jsArtifacts.hide("ajax-loader").cmd)
HTTP.early.append(_.setCharacterEncoding("UTF-8"))
loggedInTest = Full(() => User.loggedIn_?)
S.addAround(DB.buildLoanWrapper)
}
}
Yeah?
I'm for it, modulo the FIXME of course.
-Ross
> --
> 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.
>
I mean, we are essentially doing this in some places of Lift anyways... consider ScreenRules, MapperRules and PaypalRules. In my mind at least, it seems to make sense to unify these under a single namespace and implementation idiom.
I only did a partial implementation whilst playing around with the idea to see if it would even work, so your FIXME is probably one of a bunch of things that should be cleaned up :-)
Cheers, Tim
> Pretty much :-)
>
> I mean, we are essentially doing this in some places of Lift anyways... consider ScreenRules, MapperRules and PaypalRules. In my mind at least, it seems to make sense to unify these under a single namespace and implementation idiom.
Well, you're actually breaking apart into namespaces, since you're not going to bring in Mapper or Paypal rules or the other rules objects for separate packages, yes?
-Ross
--
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.
I hear what you are saying and did take on board what we had discussed at #scalalol. There is always going to be more than one view on something, of course, that is life.
I'm not married to the idea of a separate package, im just proposing something that seems (IMO) to be a positive improvement. I was aware of the private[http] stuff currently placed within LiftRules; and it does indeed add more complexity.
Let me redefine part of what has been discussed here; I was only thinking to import a composed "Application" object in boot, because those objects were nested within the composed object, so you don't wind up polluting the overall namespace of boot. I mean, if you didn't do the import, you'd just end up with: Application.AJAX.path or whatever; that however seems to be more of a stylistic thing in usage. Irrespective of the implementation (I propose one route here), I am ultimately proposing some way of "grouping" configuration aspects together. In my mind, if you are looking for an AJAX related property, it seems logical to find it in Application -> AJAX -> whateverYouWant as opposed to the current scenario where one has to load LiftRules and use "find" in the scaladoc (at least, that is what i do as the naming is not consistent)
Whilst I do believe this needs changing, I can fully appreciate that you do not feel this need. Looking at the other responses in this thread, I am not alone. We have traditionally been "by the people, for the people" in ethos, so can I propose that this topic stay open for a little while in order to collate more feedback and possibly find a mutually agreeable route forward. If that is for change, or no change, so be it. What are your thoughts?
Cheers, Tim
As another relatively new Lift User I very much have the same impression as Christoph.
Having first class namespaces/modules in LiftRules would make it much much easier to understand.
If I for example wants to find out what ajax options are available I now have to have to read through all 1414 lines of LiftRules
including comments to try and figure out what options are ajax related and whats not.
I would very much have preferred just looking in LiftRules.Ajax instead
Best Regards
Jon-Anders Teigen
Why not take the approach of Helpers: Let one top level object inherit top level traits, end of story. To prevent them from being used elsewhere, make the traits private to the package; as opposed to sealed that won't require being in the same file. Code will stay exactly the same, but LiftRules' content will be organized into several files.
I agree with Tim on this one. I wasn't privy to the conversation in London, so I'm not sure what you're referring to when you talk about "tags", but I feel like the current LiftRules implementation is just getting too big and unwieldy. I actually don't think that this needs to be broken into a separate namespace, so we avoid the access control issues, but I think that having a well organized taxonomy of rules/vars would make it easier for people to locate things and also make the code more maintainable. LiftRules.scala is second only to S.scala in LoC and although, as you suggest, we may be able to move some unrelated items into more appropriate places, it's still going to be a really big chunk of code and most likely will continue to get bigger in the future as we refine Lift's customization.
I do agree with you that more time needs to be spent on documentation, and that this would help ameliorate some of the issues, but I feel like past a certain point we're documenting to work around some of our design decisions.
I'm relatively new to Lift. I agree with Tim that LiftRules is unwieldy due to naming conventions, but I'd also agree with David that the best solution to that may be tagging (overlapping taxonomies being a persuasive argument).
I also think singling out LiftRules for refactoring or documentation work is unfair - from my limited interaction with it, it's not /that/ complicated that it needs the extra work. A broader documentation effort would be a higher priority for me, and I'm happy to get involved.
Cheers,
D
I added some responses in-line.
Cheers, Tim
> There are a couple of issues... let me address them as I understand them:
>
> • LiftRules is a big file with lots of lines of code. So what? It's not an issue. There are few defects (none that I can remember) reported against it. It's a simple pattern repeated.
LiftRules is big. Yes. I know you do not find this to be an issue, but for mere mortals such as myself, I feel it is. Other responses in this thread concur with that. That is my opinon, and you have yours - its a subjective thing, not something that can be fact one way or the other. I never complained about any reported defects; I do not have an issue with the substance of LiftRules, just its organisation.
> • LiftRules has a combination of a lot of different types of definitions (function vars, factory makers, RulesSeq, etc.). Yeah... this should be normalized into as few different types as possible (e.g., get rid of function vars).
I agree, these should be normalised.
> • LiftRules spans a lot of ground. I'm all for a discussion about what not to put into LiftRules in the future and things that could potentially be moved from LiftRules to other places (the latter would require a lot of work.)
I am not trying to say that things shouldn't get added to LiftRules. Ironically, it provides a single point for configuration and gives a lot of flexibility within Lift... I am not trying to propose that changes. Its not about substance, its organisation.
> • LiftRules is hard to consume because there's so much stuff there... it should be divided into a taxonomy. This is a lot harder than it sounds. Figuring out what part gets put into what taxonomy is hard. Figuring out the taxonomies is hard. Figuring out when we need a new taxonomy is harder. And inevitably, someone will get grumpy because we put something in the Foo taxonomy rather than the Bar taxonomy and it'll lead to more pain. But it's easier to document the code and help guide users to the key options in LiftRules.
The point about taxonomy is true; it would be difficult. Not impossible though. People get grumpy from time to time about other choices in Lift, so I wouldn't see this as being wholly different.
> • LiftRules should be moved to a separate package and split into traits for no good reason... just because it sounds cool. Nope... it's not going to happen.
I actually rescinded this earlier in the thread and stated that I was not married to it... it was a pure suggestion.
> So, I think we need some documentation around LiftRules. Before the documentation, there's no discussion to be had and given that Tim's passionate about the issue, I nominate him to document LiftRules. Specifically, he has the following assignment to be completed before Lift 2.2-M2:
> • Completely document every option in LiftRules
> • Add @tag attributed to each option in LiftRules... tags include comet, ajax, web services, important, etc. I'll work with DavidB to include tagged information in VScalaDoc so that people can navigate the tags. That gives us taxonomy at the documentation level (that's the real issue for users), yet certain options may be tagged with multiple tags.
> • In the documentation at the top of LiftRules, identify the options that will most likely be used (the stuff in the standard boot files is a good start). This should be about a dozen or so commonly used options listed in the head. This will give most users what they need to get started.
> • A list of options that are in LiftRules and shouldn't be (e.g., stuff related to I18N of dates and times, currency, etc.)
> Once we have the above complete and we've shipped 2.2, then we can ask the question of our user base... are you able to adequately navigate LiftRules and find what you need? If the answer to the question is similar to the answers in this thread even with documentation and better ability to navigate, then I'll be open to discussing how we can further improve LiftRules.
Ok, so I understand why you "nominate" me. I am passionate about this; however, you are also fully aware of the burden of writing a book (having done it yourself) and thus also likely aware that nominating me pushes this topic into a situation where by I either stop working on the book to do this, or just drop this whole thing - again, as you are likely aware, I cannot stop working on the book. Documenting LiftRules in depth is a huge task and one that I cannot commit too because of the impact that would have on Lift in Action. I was hoping to stimulate discussion about what I felt to be positive and intuitive change, not create a documentation vs design debate.
> But, I don't want to solve a documentation problem by working really hard on changes that are most likely to be breaking to solve a problem that results in far fewer discussions on this list than other facets of Lift.
For what its worth, in my opinion documenting is far harder than coding. If it were easy and we all loved doing it, we would have done it by now.
Well, I was going to write a response but it looks like Tim has already covered the salient points in much the way I envisioned. There are two things I'd like to emphasize, however:
- As Tim said, documentation is harder than coding. I just spent almost a week working on 13 pages in Exploring Lift covering REST in Lift, and only about 20% of that time was the actual work on the PocketChange codebase. I try to add or improve scaladoc on methods and classes whenever I work on defects in Lift, and for a while I was even doing pure documentation (see some of the methods on the S class, for example), but in my experience it's a lot more work to provide examples and in-depth discussion of a particular method than to write the method itself. Scala, particularly how we use it in Lift, has a pretty high information density, so a few lines of code can require quite a few lines of documentation.
- I think it's a bit disingenuous to imply that we're either not thinking or using judgment when we debate these issues. This isn't some whimsy. After years (OK, so I can only barely pluralize that) of working on Lift, I have an appreciation for the thought and effort that's gone into its design, but I also have an idea of where things are sub-optimal.
This debate is not about, as you say, "trivializing the approach that I've taken to shepherding Lift's design". It's about making Lift better, a goal I think we can all agree on whether we agree or disagree on particular issues.
I shall wait for your other mail, but I thought I was fairly clear in my previous post that it is a simple case of logistics for me - I don't have time to write documentation for LiftRules... I am too busy with the book which is turning out to be a pretty good source of documentation for the whole framework based on reader feedback thus far. Moreover, you assume I don't know LiftRules at all; I know it very well, use it extensively most days and I trawled the entire class file before making this post in the first instance. Not to mention having studied it in detail for the book.
Tim
David,
I shall wait for your other mail, but I thought I was fairly clear in my previous post that it is a simple case of logistics for me - I don't have time to write documentation for LiftRules... I am too busy with the book which is turning out to be a pretty good source of documentation for the whole framework based on reader feedback thus far. Moreover, you assume I don't know LiftRules at all; I know it very well, use it extensively most days and I trawled the entire class file before making this post in the first instance. Not to mention having studied it in detail for the book.
Tim
On 12 Oct 2010, at 14:45, David Pollak wrote:
> I understand this. Even if we were to adopt Tim's initial suggestion without modification, all of the documentation that I've asked for would have to be written anyway, so this is less work, not more. It is also designed to let Tim learn about the class so that if in fact there are continuing issues with the maintenance or usability of LiftRules, he'd be in a much better place to make recommendations based on familiarity with the code. I'm working on a post in response to his that should shed a little more light on my first ever assignment of a task to a committer based on code that they did not originally write.
--
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.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
I've been thinking about the required documentation and I think we may want to consider another aspect of this. I'm often looking at a method somewhere else in Lift (say, S), and I want to figure out how to customize its behavior. Having accurate documentation at both the configuration and use point is going to help make this more comprehensible, and make it easier to explore the API via ScalaDoc (something I do a lot in libraries). I've already done some work adding "@see" tags to cross-reference things (see S.addSessionRewriter for an example), but currently that doesn't appear to be supported in the same way that it is in JavaDoc. In other words, this is more than just documenting LiftRules itself, this is tying all of the various classes into a comprehensive whole.