Can Yeoman generators update existing files? - yeoman

So just to give you some context, I'm trying to create a generator that will create some files (based on user input of course) as well as update some existing files in the project (like adding a new route for example).
Creating the files using this.template is no problem... the question is: is there any way to do this with Yeoman without having to read the file using Node and doing some fanciful find and replace?

Ok, so I found the answer to my question.
Addy Osmani showed me where to look in this thread on twitter, and then I later found this link which shows exactly what I need.
The gist of it boils down to two functions : readFileAsString and write. Usage is as follows:
var path = "/path/to/file.html",
file = this.readFileAsString(path);
/* make modifications to the file string here */
this.write(path, file);
Edit: I've also blogged about this on my blog.
 EDIT 1
As mentionned in comments by Toilal :
The write method doesn't exists anymore, and must be replaced by writeFileFromString (arguments are also reversed) – Toilal
EDIT 2
And then, as mentionned in comments by ivoba:
this.writeFileFromString & this.readFileAsString are deprecated, github.com/yeoman/html-wiring should be used by now, things change :) – ivoba

Yeoman also provides a more elegant way of fs operations using mem-fs-editor.
You can use this.fs.copy, passing a process function as an option to do any modifications to the content:
this.fs.copy(path, newPath, {
process: function(content) {
/* Any modification goes here. Note that contents is a Buffer object */
var regEx = new RegExp('old string', 'g');
var newContent = content.toString().replace(regEx, 'new string');
return newContent;
}
});
This way you can also take advantage of mem-fs-editor features.

In combination with #sepans' excellent answer, instead of using regular expressions, one can use some parser.
From Yeoman documentation:
Tip: Update existing file's content
Updating a pre-existing file is not always a simple task. The most reliable way to do so is to parse the file AST and edit it. The main issue with this solution is that editing an AST can be verbose and a bit hard to grasp.
Some popular AST parsers are:
Cheerio for parsing HTML.
Esprima for parsing JavaScript - you might be interested in AST-Query which provide a lower level API to edit Esprima syntax tree.
For JSON files, you can use the native JSON object methods.
Parsing a code file with RegEx is perilous path, and before doing so, you should read this CS anthropological answers and grasp the flaws of RegEx parsing. If you do choose to edit existing files using RegEx rather than AST tree, please be careful and provide complete unit tests. - Please please, don't break your users' code.
More specifically, when using esprima you will most probably require also some generator such as escodegen to generate js back.
var templatePath = this.destinationPath(...);
this.fs.copy(templatePath, templatePath, {
process: function (content) {
// here use the parser
return ...
}
});
Note that you can use the same path in from, to arguments in order to replace the existing file.
On the other hand, the downside of such parsers is that in some cases it may alter the original file way too much, and although safe, this is more intrusive.

Use var text = this.fs.read(filePath) to read from a file and this.fs.write(filePath, content) to write to a file at location.

You can use the mem-fs-editor, and call the fs.append(filepath, contents, [options]) method.
e.g.
this.fs.append(this.contextRoot + "/index.html", " <p>Appended text</p>");

Related

Yeoman pass prompt answers to subgenerator

I have a yeoman generator with some subgenerators.
I know I can pass options from the parent- to the subgenerators when calling composeWith(...).
But how can I pass the answers I get from prompting? The are not available at the point when composeWith is called.
For example I prompt in the generator for an app name and want to provide this to all the subgenerators as options?
One way to do this is using the built-in config.
In the "parent" generator:
configuring(){
this.log('Saving configuration in .yo-rc.json')
const answers = this.answers.answers()
for(const key in answers){
this.config.set(key, answers[key])
}
this.config.save()
}
In the "child" generator, to populate templates:
const templateData = {
...this.config.getAll(),
...
}
this.fs.copyTpl(
this.templatePath(),
this.destinationPath(),
templateData
)
This should be simple enough to change for your use case, eg perhaps you'd want this.config.get(something) in the child generator.
Just note this won't work across different generators; only between a generator and its own sub-generators:
The .yo-rc.json file is a JSON file where configuration objects from multiple generators are stored. Each generator configuration is namespaced to ensure no naming conflicts occur between generators.
This also means each generator configuration is sandboxed and can only be shared between sub-generators. You cannot share configurations between different generators using the storage API. Use options and arguments during invocation to share data between different generators.
Oh, found in the related questions that I could call the subgenerator after prompting, instead of the initialize-method (as in the quite outdated tutorial)

What is the recommended way to make & load a library?

I want to make a small "library" to be used by my future maxima scripts, but I am not quite sure on how to proceed (I use wxMaxima). Maxima's documentation covers the save(), load() and loadFile() functions, yet does not provide examples. Therefore, I am not sure whether I am using the proper/best way or not. My current solution, which is based on this post, stores my library in the *.lisp format.
As a simple example, let's say that my library defines the cosSin(x) function. I open a new session and define this function as
(%i0) cosSin(x) := cos(x) * sin(x);
I then save it to a lisp file located in the /tmp/ directory.
(%i1) save("/tmp/lib.lisp");
I then open a new instance of maxima and load the library
(%i0) loadfile("/tmp/lib.lisp");
The cosSin(x) is now defined and can be called
(%i1) cosSin(%pi/4)
(%o1) 1/2
However, I noticed that a substantial number of the libraries shipped with maxima are of *.mac format: the /usr/share/maxima/5.37.2/share/ directory contains 428 *.mac files and 516 *.lisp files. Is it a better format? How would I generate such files?
More generally, what are the different ways a library can be saved and loaded? What is the recommended approach?
Usually people put the functions they need in a file name something.mac and then load("something.mac"); loads the functions into Maxima.
A file can contain any number of functions. A file can load other files, so if you have somethingA.mac and somethingB.mac, then you can have another file that just says load("somethingA.mac"); load("somethingB.mac");.
One can also create Lisp files and load them too, but it is not required to write functions in Lisp.
Unless you are specifically interested in writing Lisp functions, my advice is to write your functions in the Maxima language and put them in a file, using an ordinary text editor. Also, I recommend that you don't use save to save the functions to a file as Lisp code; just type the functions into a file, as Maxima code, with a plain text editor.
Take a look at the files in share to get a feeling for how other people have gone about it. I am looking right now at share/contrib/ggf.mac and I see it has a lengthy comment header describing its purpose -- such comments are always a good idea.
For principiants, like me,
Menu Edit:configure:Startup commands
Copy all the functions you have verified in the first box (this will write your wxmaxima-init.mac in the location indicated below)
Restart Wxmaxima.
Now you can access the functions whitout any load() command

How to get the "Created by <name> on <date>" text

So the question is actually simple, but I have no idea how to approach this issue. I know this code is generated by template based on this question:
XCode automatically generated comments?
I want to use the <name> that xcode provides on each mac machine which is unique for it's user, for some types of logs.
EDIT:
This is how the swift template file looks before it's used by Xcode to create my work file:
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
Surely, there is no point in parsing it.
The question is: Does anyone knows how I can get this name using swift in my application?
I searched for an answer here/Google but so far no luck.
I don't know how to read the header. But you can do it otherwise.
First if you need the creation-date of a file, you can use the NSFileManager:
var path = "path/to/your/file/"
var fileAttribs:NSDictionary = NSFileManager.defaultManager().attributesOfFileSystemForPath(path, error: nil)!
var creationDate = fileAttribs.objectForKey(NSFileCreationDate)
Also if you need the full username, you can use the function NSFullUserName() or NSUserName(). It should return the same string as __FULLUSERNAME__
var fullUsername = NSFullUserName()
var username = NSUserName()
Sometimes in the iOS Simulator, this username is empty, but in a real app, it should work properly.
That text written at template instantiation time — that is, when you create a new Xcode project (or a new file in an existing project using the File > New > File... templates). You can't read the contents of the source file your code was compiled from. (Well, unless you ship that file along with your compiled binary, and read it in like any other text file.)
But that's just text substitution — it can be done anywhere in the file, not just in the comment headers. So you could create your own file or project templates, and in the template files, put those substitution macros in code instead of in comments:
let schmoeWhoCreatedThisFile = "___FULLUSERNAME___"
Here's a tutorial found in a couple seconds of web searching that has the full details on creating templates and the substitution macros you can use in them.
Remember, substitution happens when you create a new file or project — if you're looking for who made the latest change to your source file or who built the app that shipped to your customers, you're barking up the wrong tree. Some of those sorts of things you can do with source control; others are more a matter of (human-defined, human-executed) policy for you or or your organization.

How can I load a local file from embedded Rhino?

I'm using Rhino's context.evaluateString() to run some simple JavaScript from inside of Java. It's textbook right out of the Embedding Javascript guide:
String script = // simple logic
Context c = new ContextFactory().enterContext();
ScriptableObject scope = context.initStandardObjects();
Object o = context.evaluateString(scope, script, "myScript", 1, null);
ScriptableObject result = Context.jsToJava(o, ScriptableObject.class);
I'm not sure this is the current best-practice, because the main Rhino docs appear to be down, but it's working so far.
I'd like to be able to refer to a library in the working directory -- I see that Rhino shell supports load but I don't think this works in the embedding engine.
Is this possible? Is it documented anywhere? Ideally, I'd like to be able to just call something like load('other.js') and have it search directories I specify as a global property.
I have a sort-of answer that I don't really like, not least because it exposes what I'm pretty sure is a Rhino bug that drove me crazy for the last half hour:
eval("" + Packages.org.apache.commons.io.FileUtils.readFileToString(
new java.io.File("/the/local/root", "script.js");
));
{ That "" + ... is how I work around the bug -- if you eval() a Java String (such as is returned from the readFileToString call) without manually coercing it to a JavaScript native string, nothing appears to happen. The call just silently fails. }
This blindly reads an arbitrary file and evals it -- of course, this is what you do when you eval() from the Java side, so I don't worry about it too much.
Anyway, it's not elegant for a number of reasons, but it works. I'd love to hear a better answer!

Best practices to develop and maintaing code for complex JQuery/JQueryUI based applications

I'm working on my first very complex JQuery based application.
A single web page can contain hundreds of JQuery related code for example to JQueryUI dialogs.
Now I want to organize code in separated files.
For example I'm moving all initialization dialogs code $("#dialog-xxx").dialog({...}) in separated files and due to reuse I wrap them on single function call like
dialogs.js
function initDialog_1() {
$("#dialog-1").dialog({});
}
function initDialog_2() {
$("#dialog-2").dialog({});
}
This simplifies function code and make caller page clear
$(function() {
// do some init stuff
initDialog_1();
initTooltip_2();
});
Is this the correct pattern?
Are you using more efficient techniques?
I know that splitting code in many js files introduces an ugly band-bandwidth usage so.
Does exist some good practice or tool to 'join' files for production environments?
I imagine some tool that does more work than simply minimize and/or compress JS code.
Some suggestions I might add:
keep all your variables in a globally available, multi-structured object, something like: MyVars = { dialogs: {}, tooltips: {} } and then use that across all your scripts
use call or apply methods for dynamically calling custom function names,if you perhaps want to keep the above object lightweight
For tidying things up, you could read this: http://betterexplained.com/articles/speed-up-your-javascript-load-time
This sounds fairly okay too me. Just two notes:
Use descriptive method names. "initDialog_1" doesn't tell you anything about the dialog it initializes.
While keeping JS code split into several files eases development it harms the felt performance of your interface. You could merge all files into one during build/deployment/runtime of your app. How to do it best heavily depends on your environment though.
I'm working on something fairly complex in JS right now, and have been wondering the same thing. I looked at various "module" implementations but while they look "cool" they don't seem to offer much value.
My plan at this point is to continue referencing lots of script files from my .html page (the plan is to only have one .html page, or very few).
Then when I'm building the release version, I'll write a very simple tool to fit into my build process, which will discover all the scripts I reference from the .html pages and concatenate them into one file, and replace the multiple <script> elements with a single one, so that only one request is necessary in the "release" version.
This will allow the compression to work across all the script text instead of on each separate file (like doing tar followed by gzip) and should make a difference to the script download time (though I should stress I haven't actually implemented it yet).
You usually want to keep all of your javascript inside one file. Less HTTP requests is usually better. If you take a look at the jQuery source, you'll notice that every function and property is right there in the jQuery global object:
jQuery.fn = jQuery.prototype = {
init: function(){ ... },
animate: function() { ... },
each: function() { ... },
// etc
}
However, the pattern you seem to be interested seems similar to the "module" pattern. The YUI framework uses this pattern, and allows developers to "require" different components of the library from the core module via HTTP request. You can read more about YUI here:
http://developer.yahoo.com/yui/3/yui/

Resources