dmill-bz / gremlin-console-js

A gremlin console for browsers. This console can connect either directly to gremlin-server or serve requests through a intermediary page via AJAX.
Other
21 stars 9 forks source link

cleaning up #1

Closed dmill-bz closed 8 years ago

dmill-bz commented 8 years ago

Hey @jbmusso

I was hoping you could explain a couple of things so that I can tidy this up a bit. I'm a little confused about the difference between imports and require and how export works. For example I use a require here https://github.com/PommeVerte/gremlin-console-js/blob/master/src/DriverClient.js#L1 but that's about the only one. Is it safe to assume that you should use require for third party modules?

Also I'm not really sure how to make my objects accessible in the browser. Currently doing : https://github.com/PommeVerte/gremlin-console-js/blob/master/src/index.js#L4 but this just doesn't look right.

Any help would be appreciated. Thanks!

PS: Once I manage to clear this out a bit and get some more quality in I'll set up tests and docs for this + npm. I also have a couple of plugins for this that will allow viz features and console output.

jbmusso commented 8 years ago

Actually you should be able to use import .. from syntax with most modules from npm, and you can do so with gremlin-javascript. require() and module.exports is how Node.js historically imports and exports modules by using the Common.js spec. This is not part of the JavaScript standard for the language didn't specify how to handle modules until the EcmaScript 2015 standard which was published in June 2015 (and the addition of import and export). Before Node.js, JS was a browser-thing only and imports were done by using multiple <script> tags. Require.js is another way to handle modules, before the ES2015 standard. Famous tools such as browserify and later webpack made it possible to bundle several files into one using the Common.js spec into one. This is how npm, which was historically for Node.js/back-end only packages, became a reality for front-end web development.

What's going on under the hood when using Babel is that import and export statements are being transpiled to require() and module.exports (Common.js spec). I think this is the default behavior which can be configured with Webpack and Babel (I never really had to tweak that). Same goes when using Babel server side with Node.js: import and export statements are currently transpiled to require() and module.exports because Node.js/V8 engine currently do not support this part of the EcmaScript 2015 standard. Note that there are subtle differences between Babel 5 and Babel 6 (see especially this: https://medium.com/@kentcdodds/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution-ad2d5ab93ce0#.hxzssypki): I've had this issue server-side and I think this can happen when bundling front-end assets.

I may not be 100% accurate but this should give you a cleaner picture.

Regarding the way you can make your objects accessible in the browser, this is the responsibility of Webpack when bundling your application as a Universal Module (UMD, Universal Module Definition), which you are currently doing. You shouldn't have to mutate the global object yourself. However, it is very likely that your users will consume your library by using tools such as Webpack or Browserify and will just add it as a dependency to their package.json and use require() or import themselves. Bothering with UMD is mostly useful if you really want people to use your library via <script> tags, which is currently rarely useful (until HTTP2 becomes a thing) unless for prototyping.

By looking at your current webpack.config.js file, you're telling Webpack to expose the library as gremlin-console. This is not a valid JS identifier name (because of the dash -) and it won't be exposed globally (via the globals object), though it's a valid Object property name which can be only accessed with the Object bracket notation (because of that dash). To put it simply:

// That won't work (invalid identifier name): - is the minus arithmetic operator here
gremlin-console.doSomething();

// That won't work (object dot notation): same, - is the minus arithmetic operator
globals.gremlin-console.doSomething();

// This should work (object bracket notation) though it's ugly and you don't want that
globals['gremlin-console'].doSomething();

This may be a shot in the dark, but can you try renaming your module in your Webpack config file to gremlinConsole or GremlinConsole?

dmill-bz commented 8 years ago

Great! Thanks a lot for all the information. gremlinConsole worked as you said it would. That actually cleared up a lot of confusion for me. I'm going to go ahead with the documentation now that I've straightened these points out.

One last point. I've noticed that I had to build gremlin manually before I could go ahead with building the console umd file. Is this because I'm pulling it from github rather than npm in the package.json? (requires master branch atm)

jbmusso commented 8 years ago

You have to build gremlin because its current entry point is lib/index.js in the package.json. The /lib folder isn't versioned, though it is published to npm (see .npmignore - actually, I shouldn't publish /src there as well).

I'm doing so in order to ensure the end user doesn't have the burden of building/compiling from ES2015/2016 sources: it's all transpiled to EcmaScript 5 which runs pretty much anywhere now. The downside of this approach is that someone consuming gremlin on a recent platform (ie. Node v5.9.x) will be executing EcmaScript 5 code although it can actually run pretty much all of ES2015. Babel 6 added support for selective/progressive transpiling depending on the target runtime (have a look at Babel presets, this is really smart). I think it's still open for debate on how lib authors writing in ES2015/2016 code should publish their libraries. I might add an option that allow the end user to compile the source code according to the targeted runtime.

dmill-bz commented 8 years ago

Ok that makes sense. Thanks for the info :)

dmill-bz commented 8 years ago

From experience are there any good promise libraries you would suggest? (I'm assuming Promise isn't native)

jbmusso commented 8 years ago

Promise was added in EcmaScript 2015, so it's standard for modern browsers and runtimes (I think it was added in Node.js v4). There are several Promises A/+ compliant libraries that you can use, including Bluebird or Q. These libraries offer A/+ compliant Promise as well as many higher levels methods which make it easier to use promises (ie. concurrency, etc.). I tend to use Bluebird on my projects - if I recall correctly, it uses the native Promise implementation if found, else it falls back to its own implementation (have to check that). Unsure about Q, it's been around for a while and it's also top-quality. Both are very solid. Bluebird has always been emphasizing on performance.

For example, Bluebird's Promise.promisifyAll() is absolutely awesome ("Promisifies the entire object by going through the object's properties and creating an async equivalent of each function on the object and its prototype chain"). This is not part of the standard, but definitely handy.

Compliance with the A/+ standard (now part of EcmaScript) is important because there are Promise-like objects which aren't truely promises, including jQuery's (you don't want to get bitten by that, see https://blog.domenic.me/youre-missing-the-point-of-promises/ for a very nice write-up on the topic from one of the champions who pushed Promises into the standard).

When authoring a JS lib, I think a best practice is to let the end user supply its own Promise object (native or third party): just assume the user has Promise, and let her/him polyfill the way s/he wants if missing. This will remove a dependency from your project (smaller overall bundle size). If you're forced to use higher level methods from a third-party within your library (or simply when coding and application), then it's obviously fine to add that library as a dependency to the package.json file.

When dealing with Promises, the next step is to use async functions which are a stage-3 proposal for addition to the EcmaScript standard (although they are criticized - yet again, an excellent article on the topic). Async functions returns promises and the await keywords allows you to wait for values in a non-blocking way. This is extremely useful and can be transpiled with Babel transforms using Bluebird's co-routine and ES2015 generators (generators that yield Promises, basically), and it basically works on browsers up to IE9 (and maybe IE8 with some tweaks).

Next step is to use Observable, which are kind of Promises with several/infinite number of values. See RxJS. Observable is currently a stage-1 proposal, and upcoming RxJs v5 tries to comply to the proposal. They are very powerful.

For a nicer write-up of the above (sync/async, single/multiple values), I strongly suggest reading A General Theory of Reactivity by the author of Q, Kris Kowal.

dmill-bz commented 8 years ago

Awesome JB. Thanks so much for the thorough answer. You've saved me a ton of time! This repo is mostly only lacking documentation at this point since I'm done with tests. So I'll go ahead and close this issue.

Cheers!

jbmusso commented 8 years ago

My pleasure! Async in JavaScript can bite.