Skip to content
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

have a post processing script in the root of the template #2

Closed
tbarker9 opened this issue Apr 30, 2013 · 26 comments
Closed

have a post processing script in the root of the template #2

tbarker9 opened this issue Apr 30, 2013 · 26 comments

Comments

@tbarker9
Copy link
Contributor

Some project structures require a little more than just downloading files (maven, grails, etc.). If there was a root level script (maybe called lazybones.groovy?), post processing could adjust the files appropriately. Maybe it could even receive arguments for further customization.

@pledbrook
Copy link
Owner

This is a good idea. It will require several pieces I think:

  1. A way to pass arguments on the command line to the template, either via '-D' or '-P'. I'm tempted to go with the Gradle convention of '-P'. I suspect at this point it may be worth adding a proper argument parser.
  2. Some convention on documenting the supported options/settings for a template.
  3. A custom Groovy Script class that has method ask(message, propertyName, defaultValue) (or similar) that will ask the user for a value if it hasn't already been set through a '-P' option.
  4. The custom Script class should also have a simple API for performing property substitution on template files.

I suggest just using SimpleTemplateEngine for the property substitution. It's easy to use and flexible enough.

@robfletcher
Copy link

Since lazybones itself has a fairly simple set of arguments would it be possible to just pass additional arguments to the template like Yeoman does?

@pledbrook
Copy link
Owner

Example? I don't have a problem with that, but it can be awkward to determine whose args are whose when one or more of the lazybones args are optional.

@robfletcher
Copy link

https://github.com/yeoman/generator-angular#options-1

On Tue, Apr 30, 2013 at 9:05 AM, Peter Ledbrook notifications@github.comwrote:

Example? I don't have a problem with that, but it can be awkward to
determine whose args are whose when one or more of the lazybones args are
optional.


Reply to this email directly or view it on GitHubhttps://github.com//issues/2#issuecomment-17214339
.

@pledbrook
Copy link
Owner

So that makes sense for boolean type options, but I was thinking of key-value pairs with the -P option. For example, for variable substitution into templates. The disadvantage of -P and -D options is that Windows batch files inconveniently strip out the '='! Grails suffers from this, although I think that's mainly because you have grails.bat calling startGrails.bat.

We can probably do something similar to Grails where options such as --coffeescript are added to an options map as [coffeescript: true] whereas -PgrailsVersion=2.2.1 is added as [grailsVersion: "2.2.1"].

@tbarker9
Copy link
Contributor Author

So another idea I had.... which I think might be too ambitious, is supporting multiple languages. So a template would have 1 lazybones script at the root, but it could be lazybones.rb, lazybones.groovy or even lazybones.sh. Having a bash option, albeit not OS agnostic, might be an easier choice for certain templating problems. Of course I could just call a bash script in groovy too ;).

@tbarker9
Copy link
Contributor Author

Also... how far do we go. If lazybones can support arguments, should it resolve something other than templates? Should we resolve directory / package names as well? I can't tell if this should be handled 100% by the template author with their lazybones script. Also most people use IDEs for JVM projects.... maybe it isn't unreasonable to assume that the user can do some refactoring themselves. And finally, if the lazybones bones author is fully responsible managing templating outside of downloading and filtering files, how can lazybones promote code reuse? Given that groovy has grape, maybe this isn't such a big deal.

@tbarker9 tbarker9 reopened this Apr 30, 2013
@tbarker9
Copy link
Contributor Author

I like the the idea of putting everything into an options map as @pledbrook described. If we do that do we really need -P or -D? just do --grailsVersion=2.2.1. If there is no key value pair (ie '=') then you revert to storing it as a boolean.

@pledbrook
Copy link
Owner

On the options front, I prefer standard behaviour where possible. Unfortunately, there are several competing standards 😄 @tbarker9 The one you suggest is pretty much the Grails approach. The downside is that no argument parsing library can handle it IIRC. But then the 'parsing' logic isn't particularly complicated based on regular expressions. And I don't think any arg parsing library can handle dynamic options anyway (ones that aren't known in advance).

So perhaps the Grails approach is the best one.

As for multiple languages, that sounds like a step too far at this point in time. Keep it simple and narrowly focused until there is an identified need. The simpler it is, the easier it is to maintain - and that is a guiding principle of the project.

That said, your point about promoting reuse is well taken, although perhaps I have a different interpretation. Filtering out files, variable substitution into files, etc. are things that lazybones can provide support for. But what happens if a template requires a particular feature that isn't in the version of lazybones the user is running?

I think it would be nice to have checkFeature() method available to scripts that allow them to verify whether a supported method or property is available or not. It can then decide whether to fail or not. That will also mean being diligent in maintaining @SInCE docs for the helper methods and properties.

@tbarker9
Copy link
Contributor Author

Maybe for arg parsing lazybones should use the most popular one... even if it is not the coolest one;) Since most users of lazy bones will probably use gradle, and grails is moving to gradle, I suppose the -P convention makes more sense. Also, you have to use gradle to package the template so why deviate from the norm.

I am trying to decide what I want lazybones to be. Its simplicity and integration with bintray put a big smile on my face. If it can handle variable and basic template filtering I will definitely use this. That said, I need to manipulate directory / packages based on passed variables; maybe the tools of the underlying platform are better for this.... or use the root script.... or force the user to manipulate this stuff themselves with an IDE. After all, it's just a template.... or a starting point. I think if the feature set for lazybones are well defined and fixed checkFeature might be unnecessary.

So here is the next question..... what should the root post processing script be? If the feature set for lazybones is well defined, should it be meta data / DSL about what files are included / excluded and filtered. Maybe add some processing hooks as closures such as postFilter, preFilter. Basically you would run the script, gather your defined variables and act appropriately. Or should it be directly called after the template is downloaded while containing helpful methods such as ask or filter or whatever. In the spirit of simplicity I think the last option is less disruptive and more in line with lazybones objectives.

As for code reuse, it is so easy to address either by importing remote scripts or grape that I am not sure it is worth pursuing. Also if your template has an insanely complex root script... you are probably doing it wrong. Complex scaffolding / templating should probably be left to a build tool or custom processing.

@pledbrook
Copy link
Owner

Or should it be directly called after the template is downloaded while containing helpful methods such as ask or filter or whatever. In the spirit of simplicity I think the last option is less disruptive and more in line with lazybones objectives.

This. I don't think a DSL will add much, while adding the maintenance burden. I'm not even sure whether to have the helper methods on the script class or on a utility class such that they can be statically imported. The latter has the benefit of a namespace, but the former can store state. In fact, I think an ask() method would probably want access to the options map.

So, here's what I think should happen:

  1. Lazybones pulls the packaged template from wherever it's lurking
  2. The package is expanded into the target directory
  3. Any lazybones.groovy file in the root of the target directory is loaded as type LazyBonesScript
  4. The script is executed and the lazybones.groovy file removed
  5. The README is displayed.

A LazyBonesScript should look something like

class LazyBonesScript extends Script {
    final Map options = [:]

    def ask(String message, String optionName, defaultValue) {
        ...
        return this
    }

    def filterFiles(String filePattern, Map substitutionVariables) {
        ...
        return this
    }

    boolean hasFeature(String featureName) {
        return this.getClass().methods.any { it.name == featureName }
    }
}

More methods can be added later if common tasks are discovered in the future. But lazybones does not dictate how the post-install script should work or what it should do. That's not its job.

@pledbrook
Copy link
Owner

Maybe for arg parsing lazybones should use the most popular one... even if it is not the coolest one;) Since most users of lazy bones will probably use gradle, and grails is moving to gradle, I suppose the -P convention makes more sense. Also, you have to use gradle to package the template so why deviate from the norm.

Except users of Lazybones may not be into Gradle. I'm definitely cooling on the '-P' approach. It's ugly and I don't think the reasons for its use in Gradle apply here. The only other possibility is something like

lazybones create ratpack 0.1 grails-realtime --grailsVersion 2.2.1 --coffeescript

I actually prefer the syntax, but I don't think regular expression matches would help parse it. Still, shouldn't be too hard as long as we require that all options come at the end of the normal create args.

I just realised we should probably add at least a --verbose/-v option to lazybones itself for help in diagnosing issues.

@tbarker9
Copy link
Contributor Author

If user may not be into gradle should lazybones use it to upload a template? I understand its immediate need to pound something out useful and fast.

I would say I am kind of indifferent. I am also java trained and am more comfortable with ugliness like this more than I should be ;) Haven't used the groovy cli helpers enough to know how hard this would be to parse, can't imagine it would be too hard by hand though. I assume all arguments after create args must either be flags starting with - or a key value pair like --grailsVersion 2.2.1 where the second argument does not start with -.

@tbarker9
Copy link
Contributor Author

The script you proposed seems solid to me. I assume you would use it as the base to the root script... the user wouldn't extend it. Might even open doors in the future to swap in different base scripts..... essentially a micro plugin I guess. Could be handy for certain scopes... maybe grails projects could have a different base script vs other projects. Way in the future if ever. Fun to think about.

@pledbrook
Copy link
Owner

The creator/uploader of a template is a small subset of the users of that template (hopefully). And to be honest, there's nothing that requires a template author to publish the template through Gradle. It's just the tool I use for building and publishing Lazybones itself, + any templates I create.

And yes, LazyBonesScript (or perhaps LazybonesScript - I really must choose a capitalisation regime) would be the base implementation for lazybones.groovy. Groovy allows you to specify a custom script class when loading a script. So the template developer shouldn't even know the custom script class exists, just that he or she has access to options, ask(), etc.

@tbarker9
Copy link
Contributor Author

Ah, great point. lazybones may give you a tool to upload templates, but that isn't its primary job, got it. Use gradle, groovy or just drop it in a http friendly vcs. Whatever.

Hah, that is a humdinger. LazybonesScript looks weird, but lazyBones.groovy looks weird as well.

@tbarker9
Copy link
Contributor Author

tbarker9 commented May 1, 2013

I may give LazyBonesScript a shot today with a couple of assumptions:

  1. arg parsing happens somewhere else, ie options will be set by another class
  2. in this cut I don't think I will do any logging (and maybe it won't be needed anyway). Just don't know how you want to handle logging yet.
  3. add one more method filterFiles(String filePattern) that will directly use options for filtering

I think I will use antbuilder file scanner for file filtering. If there is a more appropriate solution, maybe we can pursue it later on.

@pledbrook
Copy link
Owner

  1. Arg parsing will happen in the main class
  2. We should start using Groovy's @Log.
  3. OK

If you want to use AntBuilder, you will need to add groovy-ant as a dependency. It might be possible to use Spring's Resource abstraction too if it doesn't require too many other dependencies.

One thing I'm not sure about is whether AntBuilder will work alongside SimpleTemplateEngine. Let's see! And thanks for contributing.

@tbarker9
Copy link
Contributor Author

tbarker9 commented May 2, 2013

I was thinking of using the filescanner just to grab files, then filter them in place by hand with SimpleTemplateEngine. So the workflow would be: find files with ant based filter (aka filescanner) -> create template from them -> erase template file -> write new result. I feel a little weird filtering files in place though.

@tbarker9
Copy link
Contributor Author

tbarker9 commented May 2, 2013

You can see what I mean here: https://github.com/tbarker9/lazybones/blob/lazyscriptroot/src/main/groovy/uk/co/cacoethes/lazybones/LazyBonesScript.groovy. Once I implement ask and do some end to end unit tests I will put in a pull request.

@pledbrook
Copy link
Owner

Pull request applied, but of course we still need to invoke the post-installation script 😄 And before this feature goes into a release, we need a packaged template that includes such a script such that we can add an integration test.

@tbarker9
Copy link
Contributor Author

tbarker9 commented May 7, 2013

Understood, see my comment at the end of #14

@tbarker9
Copy link
Contributor Author

tbarker9 commented May 8, 2013

I moved the command line options to #17. Figured there is enough going on in this issue without that.

@tbarker9
Copy link
Contributor Author

I should get this implemented by tomorow. Thanks for the integrated test harness, this is going to be really handy. Unless someone has a better idea I am going to create a basic gradle / groovy template that will include some basic questions and file filtering.

@pledbrook
Copy link
Owner

I'm happy to close this now as the basic feature is now there. Any further changes should have their own issues. Sound OK?

@tbarker9
Copy link
Contributor Author

tbarker9 commented Jun 7, 2013

👍

@tbarker9 tbarker9 closed this as completed Jun 7, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants