Donāt forget the testing area. The Velocity does not seem the way to go anymore.
Itās definitely an awkward time to jump into a new app, no doubt.
However, if you look at the landscape in the next 6 months you can optimize for that. Hereās my recommendations:
Use React
React isnāt the ābestā framework but it has a huge mindshare and community to pool from. The next Blaze 2 will have a thin facade over React to put templates in separate files.
If you prefer Blaze 2, then youāre still going to have to learn React if you want to debug it. You might as well just dive in now. Remember, you can always move the JSX out later if you still dislike it. (Iāve found that once you use React youāll break it down into so many small pieces that having 2 files for every piece is a major pain).
React makes you think different. Way different. It follows the functional paradigm and helps you organize your code for the better (IMHO).
Use testing tools for the future
Velocity is on itās way out. At best it will stay and be made less buggy. However, I doubt that.
-
End to End testing can be handles with ChimpJS, Capybara, or your favorite tool as they donāt need to know about Meteor itself (though Chimp has some killer Meteor integration for setup/teardown!)
-
Unit tests can be setup to use Karma outside of Meteor with a simple config that mimics Meteors load order. Iāve used this with great success and it allows me to get millisecond test results with any JS testing tool (Jasmine, Mocha, Tape, etcā¦). Simple Jasmine stubs allow you to test a lot of client and server code without needing to mock out all of Meteor.
-
Integration tests are the most tricky as they need to know about Meteor. Gagarin is the best option out now and works well. Little magic and little to break. Youāll likely have only a few of these tests if you opt to cover most of the code with Unit and End-to-End tests.
Use a module pattern
ES6 modules are coming. You can use Webpack if you need the fast reloads or the power of configuration. However, many do not. You can mimick the modules so that when 1.3 comes out, a transition will only take a few mins or hours tops.
Think about your code as modules. Everything should be in a module namespace and the folder name should mimic this for ease of use. Doing it this way also mitigates load race conditions.
For example:
// client/helpers/form.js
const FormHelper = {
validateNumber(str) {
// ...
return true;
},
validateEmail(str) {
// ...
return true;
}
};
// expose globally pre-1.3
this.FormHelper = FormHelper;
// later in another file
const {validateNumber} = FormHelper;
validateNumber('1 555-222-0000');
// or
FormHelper.validateNumber('1 555-222-0000');
Soon you can find/replace the top of your files with 2 lines like this:
// client/helpers/calculator.js
const FormHelper = {
validateNumber(str) {
// ...
return true;
},
validateEmail(str) {
// ...
return true;
}
};
// export in a module
export default FormHelper;
// later in another file
import {validateNumber} from 'client/helpers/form';
import FormHelper from 'client/helpers/form';
validateNumber('1 555-222-0000');
// or
FormHelper.validateNumber('1 555-222-0000');
Can you share more on this and/or point to a sample repo? Sounds really attractive for plain jane unit testing.
Where was that official unofficial announcement that React will replace Blaze going forward? People keep saying that but Iāve assumed it was mostly speculation.
ROTFLMHAO!!!
Sure. Iām hoping to do a screencast on the whole workflow testing my http://react-ive.meteor.com/ project. However, as soon as I record it itās going to be outdated in a few months with 1.3 Perhaps iāll use it as a second screencast for migrating to 1.3.
Anyhow, the basic gist is that you want to load all of your files with the same rough pattern that Meteor uses (in the files array). Itās easiest to start with one single folder/file and make sure that works then work outwards until you get the hang of Karma and how itās importing (so everything isnāt blowing up at once basically).
Additionally when you discover this process of things āblowing upā, youāll slowly build up stubs that have empty functions (like Meteor.user
). This is what the tests/karma/mocks.js
file is doing. Adding just enough so that it doesnāt blow up. Then with jasmine I might do something like this:
spyOn(Meteor, 'user').and.returnValue({profile: {name: 'Adam'}});
so that in my unit tests the user is pre set and only has enough to make the unit tests pass.
Hereās an older gist focused on React that has all the files in my tests/karma
directory. Note, the package.json file for packages.
https://gist.github.com/AdamBrodzinski/aeb5b669a74259da8af8
Also hereās the karma config for this. Note, you may not want to import all of the meteor files with karma like this because this is meant for unit testingā¦ you may pull in things that you donāt want executed and at best those files would be integration tests. I tend to just manually add them in the config one at a time (or a folder) and all React components since theyāre easy to unit test.
// tests/karma/karma-config.js
module.exports = function(config) {
config.set({
basePath: '../../',
frameworks: ['jasmine', 'jquery-2.1.0'],
plugins: [
'karma-babel-preprocessor',
'karma-jquery',
'karma-jasmine',
'karma-mocha-reporter',
],
autoWatch: true,
reporters: ['mocha'],
files: [
'tests/karma/mocks.js',
'tests/karma/node_modules/react/dist/react-with-addons.js',
'tests/karma/spec_helper.js',
// import lib code first
'client/lib/*.js',
'both/lib/*.js',
// import a file before all else if manually if it fails
'client/utils.js',
// you could tech. load each folder manually here if you *have* to
'client/alpha',
'client/bravo',
// otherwise load all js code #yolo
'client/*.js',
// import all tests in client dir, this allows you to have them in
// client/components/widget/tests/widet_spec.js
'client/**/*_spec.js',
],
preprocessors: {
'**/*.js': ['babel'],
'**/*.jsx': ['babel'],
'**/*_spec.js': ['babel']
},
babelPreprocessor: {
options: {
sourceMap: 'inline',
// mimics features Meteor supports, poss. out of date now?
whitelist: [
"react",
"flow",
'es3.propertyLiterals',
'es3.memberExpressionLiterals',
'es6.arrowFunctions',
'es6.templateLiterals',
'es6.classes',
'es6.blockScoping',
"es6.properties.shorthand",
"es6.properties.computed",
"es6.parameters.rest",
"es6.parameters.default",
"es6.spread",
"es6.destructuring",
"es6.constants",
"es7.objectRestSpread",
'es7.trailingFunctionCommas',
]
},
// can't remember what this is for? perhaps unneeded lol
filename: function (file) {
return file.originalPath.replace(/\.js$/, '.es5.js');
},
sourceFileName: function (file) {
return file.originalPath;
}
}
});
};
Itās true, I got a bit ahead of myself there. There has been so much activity and good discussion that just staying on top of the Blaze thread could be a full time job, and when it comes to posting I am a fairly slow and deliberate writer. Iām going to keep working on posting more, but I have to strike the right balance between that, in-person conversations with customers, and the day-to-day work of running MDG.
Iāve tried to use this pattern in my app with something like this:
// in both/models/items.js
const Items = new Mongo.Collection("items");
// other things and methods
this.Items = Items;
then in a jsx:
// in client/components/Items/Items.jsx
const Items = this.Items;
const Component = React.createClass({
// ...
});
this.Component = Component;
But when I try to access Items (e.g. Items.findOne()
), it says Items is undefined. If I move models to /lib it works, but I canāt āimportā react components because of the same problem (it says theyāre undefined). Is this a load order problem, or Iām doing something wrong?
Iām just learning meteor/js and I can live with the long rebuild time (of course a faster one will be good, but webpack:webpack seems a little complicated). My main concern is to use a pattern which will enable an easier migration to 1.3ās module pattern.
Thanks!
Ah, yea I should have explained that better. If you have the āimportā at the top of the file, then it has to be defined at the point that file is executed. You can wrap the entire file in a Meteor.startup
function and that will solve it. Also you can āimportā it inside of the component right before you use it (though this can be time consuming to migrate).
For example:
// in both/models/items.js
const Items = new Mongo.Collection("items");
...
this.Items = Items;
and in your component file:
// in client/components/Items/Items.jsx
const Items = this.Items;
const Component = React.createClass({
getInitialState() {
return {
foo: Items.findOne();
}
}
});
this.Component = Component;
This way itās not being called until after itās defined. You could also do this:
// in client/components/Items/Items.jsx
Meteor.startup(function() {
const Items = Items;
const Component = React.createClass({
getInitialState() {
return {
foo: Items.findOne();
}
}
});
Component = Component;
}); // end Meteor.startup
However, I would go a step further and organize your collections into their own modules. Models consume them but they are different things. You could make one collections file like this:
// in both/collections/index.js
const Items = new Mongo.Collection("items");
const Posts = new Mongo.Collection("posts");
this.Collections = {
Items: Items,
Posts: Posts,
};
Then in your files you can do this:
// in client/components/Items/Items.jsx
Meteor.startup(function() {
const {Items} = Collections;
// in 1.3 would be import {items} from 'boths/collections';
const Component = React.createClass({
getInitialState() {
return {
foo: Items.findOne();
}
}
});
Component = Component;
}); // end Meteor.startup
Or since this example is a module (not just a single export) we could do this without the wrapper like this:
// in client/components/Items/Items.jsx
const Component = React.createClass({
getInitialState() {
const {Items} = Collections; // now it's defined at call time
return {
foo: Items.findOne();
}
}
});
Component = Component;
Hope this helps! I need to write this up in a blog post soon
Will you also consider a screen cast on migrating from Blaze 1 to React from the bottom up (not the top down)?
Iām talking taking a list within a Blaze 1 template, and converting it over to React ā all the while keeping any communication between the new React list and the Blaze 1 template.
Will you also consider a screen cast on migrating from Blaze 1 to React from the bottom up (not the top down)?
I prob. wonāt have to do this but this video explains how to do it really well!
What does the above mean?
Anyway. Iāve seen this video, which is how I got the idea of starting from the bottom up. But what I and many others I think would like to see is this applied to Blaze 1.
Replacing a portion of a Blaze 1 Template with a React component (and this React component should be able to communicate with the Blaze 1 Template).
Something like this is badly needed for us in the Meteor community that have existing Blaze 1 applications that need migrating to React.
I first heard about Meteor about four weeks ago, on the coding blocks podcast (great podcast). They were singing its praises so I checked it out and was blown away once Iād done the tutorial. This, I thought, is the perfect platform for getting all those ideas out of my head and into a prototype - so much quicker than the .net stack Iām used to.
Then I was listening to Josh and Ben on the Crater podcast and realised that Blaze was on the way out, and also that the testing game was maybe a little weak and it definitely left me sad. I think the question marks over Blaze at the moment are a real concern to those of us coming to the framework now, which is a shame as the buzz is really growing over Meteor (based on my sample of one people obviouslyā¦!).
Iām watching this space with great interest.
You may have seen this by now, but figured Iād post it just in case.
Basically, MDG is going to create some type of Blaze abstraction on top of React so that you get the power of React and itās community but with a more Blaze like syntax. Iām really interested to see what they come up with! Should be pretty cool.
Thanks for your perspective @tomRedox. Iām glad I came to Meteor over a year ago, or else I would have second thoughts too. Everyone was on the same technical page and things were much more simple. It was nice to be able to focus on my business ideas and not the tech so much ā it was great while it lasted. Ah, the good old daysā¦
I agree with others, itās a questionable time to come to Meteor with all the transition going on. In fact, itās not just Blaze and Velocity MDG is kicking to the curb, theyāre going to be adding Facebookās GraphQL. Also look for Flux and Redux to complement or replace tech within the Meteor stack too. Also, Webpack could possibly be replacing portions of the Meteor stack. Say goodbye to Atmosphere in favor of npm. There might be more but itās hard to keep up.
MDG seems to be slowly replacing a lot of their tech stack in favor of Facebookās stack at the moment.
I have no choice but to learn the Facebook stack to keep current as Iām tied to Meteor due to a production application in use by many clients of mine.
Yea there is a lot going on in the JavaScript world. Itās still really early and everyone is figuring out how to write thick clients on the web. JS tech was optimized for āJS Sprinkesā 5 years ago.
Web apps are starting to become more like iOS/Android apps but our tooling and libraries for the past 6 years (or more) has been really poor at doing this (compared to .net, java, ios, android).
Backbone was the first paradigm shift for thick clients. React is another paradigm shift, making our UI declarative and using functional programming methodologies (the virtual dom is cool but is my least fav feature).
Also the language itself is starting to mature. We āFINALLYā have modules in the language Well almost, at least with Babel. JavaScript itself also does OO very poorly compared to Java/C#/Ruby/etcā¦ However, it does do the functional paradigm really well and is getting more āfunctionalā features in ES7/8
Anyway my point is, JavaScript is inherently going to be churning through libs and frameworks until we can figure out how to build web apps that donāt crumble. Though I hear you, I too could do with some JS stability
Any tips on using module pattern with different files sharing the same namespace?
If youāre using the āpatternā and not real modules you can do this, itās basically checked to see if the module is defined and if it is, itāll use it, otherwise Utils
will fallback to an empty object :
this.Utils = this.Utils || {};
Utils.parseEmail = function() {
};
// somewhere else
this.Utils = this.Utils || {};
Utils.parsePhone = function() {
};
Generally this is code smell because you can prob. break up the module more. For instance, why not use sub modules like this (or even use two non-nested modules):
// somewhere
Utils.Email = {
parseEmail() {}
};
// somewhere else
Utils.Phone = {
parseNumber() {}
};