I'm using Hogan.js, which is compatible with the Mustache spec.
And im having trouble implementing a solid way of doing pluralization.
I would like to keep using Hogan and use http://i18next.com/ for i18n handling
doing something like this works for the simple cases
tpl:
{{#plural(count)}}
I have {{count}} apples!
{{/plural(count)}}
data:
{
count: 2,
'plural(count)': function () {
return function () {
return _t[arguments[0].trim()][this['count']]
}
}
}
this requires parsing/scanning/rendering in seperate steps to be able to generate all of the required plural methods (plural(key.val) etc.) but thats fine, it only needs to be done once, at server boot.
this breaks on things like
{{#plural(key.nested)}}
that would match if the data looked like
{
'plural(key': {
'val)': ...
}
}
this also requires me to manually lookup the values from the context, not a major problem but there are some cases with lambda's/partials that might be impossible to resolve
for the default translation mappings, thing are a lot less complex, and thats easy to handle
Ok found the way I think is best to handle this problem:
var tpl_data = fs.readFileSync('./tpl/test.hjs', 'utf8');
var scan = Hogan.scan(tpl_data);
var tree = Hogan.parse(scan);
var gen = Hogan.generate(tree, tpl_data, {asString:false});
var out = gen.render(data);
alter the tree, replacing all tag keys to i18n
where the n matches your pattern /i18n .+/
I use {{#i18n {count: count, text: 'I have <%count%> apples!'} }} and the like to add the options for i18next
so i match all n's starting with i18n
add the i18n to Hogan.codegen
Hogan.codegen.i18n = function (node, context) {
context.code += 't.b(t.v(t.i18n("' + esc(node.n) + '",c,p,0)));';
}
add the i18n method to the prototype of Hogan.Template
Hogan.Template.prototype.i18n = function (key, ctx, partials, returnFound) {
//here the ctx is an array with from right to left the render scopes
// most right is the most inner scope, most left is the full render data
//1. get the config from the key,
//2. get the values out of the scope
//3. get the values for the translation
//4. lookup the translation, and repleace the values in the translation
//5. return the translated string
};
note that inside the Hogan.Template.prototype.i18n you can access all of the template's methods
Related
The question is related to general js programming, but I'll use nightwatch.js as an example to elaborate my query.
NightWatch JS provides various chaining methods for its browser components, like: -
browser
.setValue('input[name='email']','example#mail.com')
.setValue('input[name='password']', '123456')
.click('#submitButton')
But if I'm writing method to select an option from dropdown, it requires multiple steps, and if there are multiple dropdowns in a form, it gets really confusing, like: -
browser
.click(`#country`)
.waitForElementVisible(`#india`)
.click(`#india`)
.click(`#state`)
.waitForElementVisible(`#delhi`)
.click(`#delhi`)
Is it possible to create a custom chaining method to group these already defined methods? For example something like:
/* custom method */
const dropdownSelector = (id, value) {
return this
.click(`${id}`).
.waitForElementVisible(`${value}`)
.click(`${value}`)
}
/* So it can be used as a chaining method */
browser
.dropdownSelector('country', 'india')
.dropdownSelector('state', 'delhi')
Or is there any other way I can solve my problem of increasing reusability and readability of my code?
I'm somewhat new to JS so couldn't tell you an ideal code solution, would have to admit I don't know what a proxy is in this context. But in the world of Nightwatch and test-automation i'd normally wrap multiple steps I plan on reusing into a page object. Create a new file in a pageObject folder and fill it with the method you want to reuse
So your test...
browser
.click(`#country`)
.waitForElementVisible(`#india`)
.click(`#india`)
.click(`#state`)
.waitForElementVisible(`#delhi`)
.click(`#delhi`)
becomes a page object method in another file called 'myObject' like...
selectLocation(browser, country, state, city) {
browser
.click(`#country`) <== assume this never changes?
.waitForElementVisible(country)
.click(country)
.click(state)
.waitForElementVisible(city)
.click(city);
}
and then each of your tests inherit the method and define those values themselves, however you chose to manage that...
const myObject = require ('<path to the new pageObject file>')
module.exports = {
'someTest': function (browser) {
const country = 'something'
const state = 'something'
const city = 'something'
myObject.selectLocation(browser);
You can also set your country / state / city as variables in a globals file and set them as same for everything but I don't know how granular you want to be.
Hope that made some sense :)
This is a great place to use a Proxy. Given some class:
function Apple ()
{
this.eat = function ()
{
console.log("I was eaten!");
return this;
}
this.nomnom = function ()
{
console.log("Nom nom!");
return this;
}
}
And a set of "extension methods":
const appleExtensions =
{
eatAndNomnom ()
{
this.eat().nomnom();
return this;
}
}
We can create function which returns a Proxy to select which properties are retrieved from the extension object and which are retrieved from the originating object:
function makeExtendedTarget(target, extensions)
{
return new Proxy(target,
{
get (obj, prop)
{
if (prop in extensions)
{
return extensions[prop];
}
return obj[prop];
}
});
}
And we can use it like so:
let apple = makeExtendedTarget(new Apple(), appleExtensions);
apple
.eatAndNomnom()
.eat();
// => "I was eaten!"
// "Nom nom!"
// "I was eaten!"
Of course, this requires you to call makeExtendedTarget whenever you want to create a new Apple. However, I would consider this a plus, as it makes it abundantly clear you are created an extended object, and to expect to be able to call methods not normally available on the class API.
Of course, whether or not you should be doing this is an entirely different discussion!
I use a JSON file for common phrases so I don't have to type them and maybe in the future they can be translated. So for example in my main code I want to say You don't have the permission to use ${command_name}. This works perfectly fine hardcoded into my .js file but ultimately I want this to be in a JSON file, which does not allow any variables to be inserted.
Does anyone know a solution to my problem?
EDIT: Thanks for the suggestions. I guess string.replace would be my best option here. Wish there was some built in feature that'd convert variables in a JSON string to variables declared in that JS file.
You cannot treat template string literals in JSON files like in Javascript "code". You said it yourself. But: You could use a template engine for this - or just simple String.replace().
Example for a template engine: https://github.com/janl/mustache.js
With Mustache (as an example) your code will look like this
var trans = {
command_name: "dump"
};
var output = Mustache.render("You don't have the permission to use {{command_name}}", trans);
With simple String.replace():
var str = "You don't have the permission to use %command_name%";
console.log(str.replace('%command_name%', 'dump'));
You can simply use placeholders. The following function replaces the placeholders with user-defined values:
const messages = {
msgName: 'Foo is :foo: and bar is :bar:!'
}
function _(key, placeholders) {
return messages[key].replace(/:(\w+):/g, function(__, item) {
return placeholders[item] || item;
});
}
Usage:
_('msgName', { foo: 'one', bar: 'two' })
// "Foo is one and bar is two!"
It's just an example. You can change the placeholders style and the function behavior the way you want!
You can use config npm module and separate your JSON files according to your environment.
./name.json
{
command: "this is the output of 'command'"
}
./Node.js
cost names = require('./name.json');
console.log('name === ', name.command);
// name === this is the output of 'command'
So the main challenge is getting separated file with string constants when some of them being parametrizable, right?
JSON format itself operates on strings(numbers, booleans, lists and hashmap) and knows nothing about substitution and parameters.
You are also unable to use template strings like you don't have permission to do ${actionName} since template strings are interpolated immediately.
So what can you do?
Writing your own parser that takes config data from JSON file, parse a string, find a reference to variable and substitute it with value. Simple example:
const varPattern = /\${([^{}]+)}/g;
function replaceVarWithValue(templateStr, params) {
return templateStr.replace(varPattern, (fullMatch, varName) => params[varName] || fullMatch);
}
or you can use any npm package aimed on localization like i18n so it would handle templates for you
Basically you can implement a function parse which, given a text and a dictionary, it could replace any ocurrence of each dictionary key:
const parse = (template, textMap) => {
let output = template
for (let [id, text] of Object.entries(textMap)) {
output = output.replace(new RegExp(`\\$\{${id}}`, 'mg'), text)
}
return output
}
const textMap = {
commandName: 'grep',
foo: 'hello',
bar: 'world'
}
const parsed = parse('command "${commandName}" said "${foo} ${bar}"', textMap)
console.log(parsed)
BTW, I would suggest you that you should use some existing string templating engine like string-template to avoid reinventing the wheel.
Hopefully this question won't be flagged as too subjective but I'm newish to OOP and struggling a bit when it come to sharing data between parts of my code that I think should be separated to some extent.
I'm building a (non-geo) map thing (using leaflet.js which is superduper) which has a map (duh) and a sidebar that basically contains a UI (toggling markers both individually and en masse, searching said marker toggles as well as other standard UI behaviour). Slightly confused about organisation too (how modular is too modular but I can stumble through that myself I guess). I am using a simple JSON file for my settings for the time being.
I started with static methods stored in objects which is essentially unusable or rather un-reusable so I went for nested constructors (kinda) so I could pass the parent scope around for easier access to my settings and states properties:
function MainThing(settings) {
this.settings = options;
this.states = {};
}
function SubthingMaker(parent) {
this.parent = parent;
}
SubthingMaker.prototype.method = function() {
var data = this.parent.settings.optionOne;
console.log(data);
this.parent.states.isVisible = true;
};
MainThing.prototype.init = function() {
this.subthing = new SubthingMaker(this);
// and some other fun stuff
};
And then I could just create and instance of MainThing and run MainThing.init() and it should all work lovely. Like so:
var options = {
"optionOne": "Hello",
"optionTwo": "Goodbye"
}
var test = new MainThing(options);
test.init();
test.subthing.method();
Should I really be nesting in this manner or will it cause me problems in some way? If this is indeed okay, should I keep going deeper if needed (maybe the search part of my ui wants its own section, maybe the map controls should be separate from DOM manipulation, I dunno) or should I stay at this depth? Should I just have separate constructors and store them in an object when I create an instance of them? Will that make it difficult to share/reference data stored elsewhere?
As regards my data storage, is this an okay way to handle it or should I be creating a controller for my data and sending requests and submissions to it when necessary, even if that data is then tucked away in simple JSON format? this.parent does really start to get annoying after a while, I suppose I should really be binding if I want to change my scope but it just doesn't seem to be an elegant way to access the overall state data of the application especially since the UI needs to check the state for almost everything it does.
Hope you can help and I hope I don't come across as a complete idiot, thanks!
P.S. I think the code I posted works but if it doesn't, its the general idea I was hoping to capture not this specific example. I created a much simpler version of my actual code because I don't want incur the wrath of the SO gods with my first post. (Yes, I did just use a postscript.)
An object may contain as many other objects as are appropriate for doing it's job. For example, an object may contain an Array as part of its instance data. Or, it may contain some other custom object. This is normal and common.
You can create/initialize these other objects that are part of your instance data in either your constructor or in some other method such as a .init() method whichever is more appropriate for your usage and design.
For example, you might have a Queue object:
function Queue() {
this.q = [];
}
Queue.prototype.add = function(item) {
this.q.push(item);
return this;
}
Queue.prototype.next = function() {
return this.q.shift();
}
var q = new Queue();
q.add(1);
q.add(2);
console.log(q.next()); // 1
This creates an Array object as part of its constructor and then uses that Array object in the performance of its function. There is no difference here whether this creates a built-in Array object or it calls new on some custom constructor. It's just another Javascript object that is being used by the host object to perform its function. This is normal and common.
One note is that what you are doing with your MainThing and SubthingMaker violates OOP principles, because they are too tightly coupled and have too wide access to each other internals:
SubthingMaker.prototype.method = function() {
// it reads something from parent's settings
var data = this.parent.settings.optionOne;
console.log(data);
// it changes parent state directly
this.parent.states.isVisible = true;
};
While better idea could be to make them less dependent.
It is probably OK for the MainThing to have several "subthings" as your main thing looks like a top-level object which will coordinate smaller things.
But it would be better to isolate these smaller things, ideally they should work even there is no MainThing or if you have some different main thing:
function SubthingMaker(options) {
// no 'parent' here, it just receives own options
this.options = options;
}
SubthingMaker.prototype.method = function() {
// use own options, instead of reading then through the MainThing
var data = this.options.optionOne;
console.log(data);
// return the data from the method instead of
// directly modifying something in MainThing
return true;
this.parent.states.isVisible = true;
};
MainThing.prototype.doSomething = function() {
// MainThing calls the subthing and modifies own data
this.parent.states.isVisible = this.subthing.method();
// and some other fun stuff
};
Also to avoid confusion, it is better not to use parent / child terms in this case. What you have here is aggregation or composition of objects, while parent / child are usually used to describe the inheritance.
I'm using client-side less.js. Is there a way to get all variables from my less file. I know how to modify variables:
less.modifyVars({
"#var": "#fff"
});
But I want to get all of them, like (don't work):
var variables = less.getVars();
This is going to be an unconventional answer as it seems that this data isn't publicly exposed as part of the browser facing API.
tl;dr
Save a local copy of the less.js file.
Add this function definition somewhere
function exposeVars(root) {
var r=root._variables,n=Object.keys(r),m={}
for(var k of n){m[k]=r[k].value}
less.variables = m;
}
Add the following call exposeVars(evaldRoot) just before return result on line ~2556.
Now less.variables contains all the variables from your file.
Disclaimer: Doing this is not a good idea! It's fine if you're just playing around, debugging or testing something, but don't depend on this hack for anything serious!
The basic aim here was to find the point in the source where the .less files are turned into abstract syntax trees (or some other formal structure).
Jumping straight into the source, I found a ParseTree class. It's a reasonable assumption to guess that it will contain the result of parsing the less file.
I wrote a quick test file and added it to the browser with the appropriate tag. It looks like this:
#myvar: red;
#othervar: 12px;
body {
padding: #othervar;
background: #myvar;
}
I've downloaded a local copy of less.js and added a breakpoint added to line 2556.
I had a poke around in the local scope to see what was available and found the variables in an object called evaldRoot.
evaldRoot = {
_variables: {
#myvar: {
name: '#myvar',
value: Color
},
#othervar: {
name: '#othervar',
value: Dimension
}
}
}
Next job was to work out where this data goes. It seems like the evaldRoot variable is used to generate the resulting CSS (which would make sense, as it contains information such as variables).
if (options.sourceMap) {
sourceMapBuilder = new SourceMapBuilder(options.sourceMap);
result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports);
} else {
result.css = evaldRoot.toCSS(toCSSOptions);
}
Whatever happens, the variables goes out of scope after it is used to generate a string of CSS as result.css.
To expose of these variables, the script needs a small modification. You'll have to expose the variables publicly somehow. Here's an example of doing that.
function exposeVars(root) {
var varNames = Object.keys(root._variables);
var variables = varNames.reduce(function(varMap, varName) {
varMap[varName] = root._variables[varName].value;
}, {});
less.variables = variables;
}
This can be added just before the return statement with the breakpoint.
exposeVars(evaldRoot);
return result;
Now the variables will be available in a name: value object on the global less object.
You could even modify the expose function to return the variables from a call to less.getVars(). Just like your initial suggestion.
function exposeVars(root) {
// ...
less.getVars = function() {
return variables;
};
}
I am trying to build a set of utils for my NodeJS project. These helpers will include: text utils (like substringing, console logging etc.), and more specific helpers like parsing the text of a tweet.
So I am trying to divide the module in different files and with a very clear idea of what each thing is meant to do.
For example I would like to achieve this:
var helpers = require("helpers");
var Utils = new helpers.Utils();
// working with text
Utils.text.cleanText("blahblalh");
// working with a tweet
Utils.twitter.parseTweet(tweet);
As you can see I am using Utils for different things, by calling very specific methods and sub methods.
I tried to understand how inheritance works here but I got a little bit lost.
This is what I am doing (some rough sample code):
//node_modules/helpers/index.js
var Text = require('./text');
var Twitter = require('./twitter');
function Utils() {
}
Utils.prototype.text = {
cleanText: function(text) {
Text.cleanText(text);
}
};
Utils.prototype.twitter = {
parseTweet(tweet) {
Twitter.parseTweet(tweet);
}
};
//node_modules/helpers/text.js
function Text() {
}
Text.prototype.cleanText = function(text) {
if (typeof text !== 'undefined') {
return text.replace(/(\r\n|\n|\r)/gm,"");
}
return null;
};
module.exports = Text;
//node_modules/helpers/twitter.js
function Twitter() {
};
Twitter.prototype.parseTweet = function(data) {
return data;
};
module.exports = Twitter
Is this a correct way? Am I doing something wrong or that could slow down the performances, etc?
To clarify how I'm understanding your post, I see two questions:
How do I structure code/methods within files, files that represent a category of utility functions
How do I organize the those categorical files into one larger library
Structuring methods within a category
Rather than making all of the category specific functions methods of objects (e.g. Twitter or Text), you could just export the functions in files named after them. Since it seems you are passing in the data you want to use, there is no need to make the functions instance methods of some empty class.
If your usage patterns of Twitter or Text usually have class variables you want to keep state on, and you want to instantiate Text or Twitter objects to use your examples, then I suppose that would be appropriate. When I setup util libs in my projects it usually is a bunch of exported functions that make up a module, rather than an exported javascript class.
To provide an example of what a text.js file made up of text-based utility functions might look like:
module.exports = {
cleanText:function(text) {
// clean it and return
},
isWithinRange(text, min, max) {
// check if text is between min and max length
}
}
Alternatively, you could do it this way:
exports.cleanText = function(text) {
// clean it and return
}
exports.isWithinRange = function (text, min, max) {
// check if text is between min and max length
}
Structuring utility category files to make a larger utility library
As far as organizing the utility methods, Luca's example is nice. I've organized some similarly like this:
utils-module/
lib/
text.js <-- this is the example file shown above
twitter.js
test/
index.js
Where index.js does something like
var textUtils = require('./lib/text');
exports.Text = textUtils;
Then when I want to use the util lib in say some User model in my node API, it's simply:
/*
* Dependencies
*/
var textUtils = require('path/to/lib').Text;
/*
* Model
*/
function User() {}
/*
* Instance Methods
*/
User.prototype.setBio = function(data) {
this.bio = textUtils.cleanText(data);
}
module.exports = User;
When I was first learning it was very helpful to look at popular, well-respected libraries to see how more experienced node/javascript devs were doing things. There are so many good (and bad) ones out there!
You can see a utils library example with lodash.
Lodash is an utility lib like underscorejs.
This library have file sustem structure like your.
It divides the functions in categories. Each category is a folder with an index.js file that includes into a namespace (literal object) each functions for that category!
Lodash/
Objects/
Function1.js
Functions2.js
....
Index.js
Array/
Function1.js
...
Index.js
Then in your code you can do this:
var objectsUtils = require("lodash/objects");
var foreach = require("lodash/array/each");
You can create a similar file system structure in order to have more flexibility.
You can require the entire lib, only one namespace or a single function.
This is better for performance, because you use only what you need and have a memory usage gain.