Merge branch 'devel' into deps-radical

This commit is contained in:
David Greenspan
2013-03-04 13:46:50 -08:00
23 changed files with 144 additions and 83 deletions

View File

@@ -1,14 +1,21 @@
## vNEXT
* User documents have id's when onCreateUser and validateNewUser hooks run.
* Removed all restrictions on EJSON types in MongoDB, even user-defined ones.
* `coffeescript` package: Support literate Coffeescript files with the extension
`.litcoffee`.
* Fixed bug where an empty `fields` object was sometimes passed to a `changed`
callback of `Cursor.observeChanges`.
* Fixed `{$type: 5}` selectors for binary values on browsers that do not support
`Uint8Array`
* Stop making `Session` available on the server; it's not very useful there.
## v0.5.7
* The DDP wire protocol has been redesigned.

View File

@@ -3,7 +3,7 @@
set -e
set -u
BUNDLE_VERSION=0.2.20
BUNDLE_VERSION=0.2.21
UNAME=$(uname)
ARCH=$(uname -m)
@@ -79,7 +79,7 @@ which npm
cd "$DIR/lib/node_modules"
npm install connect@1.9.2 # not 2.x yet. sockjs doesn't work w/ new connect
npm install optimist@0.3.5
npm install coffee-script@1.4.0
npm install coffee-script@1.5.0
npm install less@1.3.3
npm install stylus@0.30.1
npm install nib@0.8.2

View File

@@ -98,7 +98,14 @@ var files = module.exports = {
// given a path, returns true if it is a meteor application (has a
// .meteor directory with a 'packages' file). false otherwise.
is_app_dir: function (filepath) {
return fs.existsSync(path.join(filepath, '.meteor', 'packages'));
// .meteor/packages must be a *file*, not a directory; future versions of
// meteor will create a directory at $HOME/.meteor which contains a
// subdirectory called packages, but this doesn't make it an app!
try { // use try/catch to avoid the additional syscall to fs.existsSync
return fs.statSync(path.join(filepath, '.meteor', 'packages')).isFile();
} catch (e) {
return false;
}
},
// given a path, returns true if it is a meteor package (is a

View File

@@ -14,7 +14,8 @@ var _ = require('underscore');
// refuse to update if we're in a git checkout.
if (files.in_checkout()) {
console.log("This is a git checkout. Update it manually with 'git pull'.");
console.log("Your Meteor installation is a git checkout. Update it " +
"manually with 'git pull'.");
process.exit(1);
}

View File

@@ -180,8 +180,21 @@ var run = function () {
var app = connect.createServer();
var static_cacheable_path = path.join(bundle_dir, 'static_cacheable');
if (fs.existsSync(static_cacheable_path))
app.use(gzippo.staticGzip(static_cacheable_path, {clientMaxAge: 1000 * 60 * 60 * 24 * 365}));
app.use(gzippo.staticGzip(path.join(bundle_dir, 'static'), {clientMaxAge: 0}));
// cacheable files are files that should never change. Typically
// named by their hash (eg meteor bundled js and css files).
// cache them ~forever (1yr)
app.use(gzippo.staticGzip(static_cacheable_path,
{clientMaxAge: 1000 * 60 * 60 * 24 * 365}));
// cache non-cacheable file anyway. This isn't really correct, as
// users can change the files and changes won't propogate
// immediately. However, if we don't cache them, browsers will
// 'flicker' when rerendering images. Eventually we will probably want
// to rewrite URLs of static assets to include a query parameter to
// bust caches. That way we can both get good caching behavior and
// allow users to change assets without delay.
// https://github.com/meteor/meteor/issues/773
app.use(gzippo.staticGzip(path.join(bundle_dir, 'static'),
{clientMaxAge: 1000 * 60 * 60 * 24}));
// read bundle config file
var info_raw =

View File

@@ -548,16 +548,12 @@ your collection should be published to which users.
{{#warning}}
In this release, Minimongo has some limitations:
* `$elemMatch` is not supported in selectors.
* `$pull` in modifiers can only accept certain kinds
of selectors.
* In selectors, dot notation may not work correctly.
* `$` to denote the matched array position is not
supported in modifier.
* `findAndModify`, upsert, aggregate functions, and
map/reduce aren't supported.
* The supported types are String, Number, Boolean, Array,
and Object.
All of these will be addressed in a future release. For full
Minimongo release notes, see packages/minimongo/NOTES

View File

@@ -153,7 +153,7 @@ can be used from both client and server code.
// server: populate collections with some initial documents
Rooms.insert({name: "Conference Room A"});
var myRooms = Rooms.find({}).fetch();
Messages.insert({text: "Hello world", room: myRooms[0].id});
Messages.insert({text: "Hello world", room: myRooms[0]._id});
Parties.insert({name: "Super Bowl Party"});
Each document set is defined by a publish function on the server. The
@@ -164,7 +164,7 @@ case is to publish a database query.
// server: publish all room documents
Meteor.publish("all-rooms", function () {
return Rooms.find(); // everything
);
});
// server: publish all messages for a given room
Meteor.publish("messages", function (roomId) {
@@ -377,7 +377,7 @@ queries are stopped, and they stop updating. For this reason, you never have to
worry about the [zombie
templates](http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/)
that plague hand-written update logic. To protect your elements from cleanup,
just make sure that they on-screen before your code returns to the event loop,
just make sure that they are on-screen before your code returns to the event loop,
or before any call you make to [`Meteor.flush`](#meteor_flush).
Another thorny problem in hand-written applications is element
@@ -574,10 +574,12 @@ Meteor environment in arbitrary ways. Some examples of packages are:
it's faster to add a package.
* The [underscore](#underscore) package extends both the
client and server environments. Many of the core Meteor features,
including Minimongo, the Session object, and reactive Handlebars
templates, are implemented as internal packages automatically
included with every Meteor application.
client and server environments.
Many of the core Meteor features,
including Minimongo, the Session object, and reactive Handlebars
templates, are implemented as internal packages automatically
included with every Meteor application.
You can see a list of available packages
with [`meteor list`](#meteorlist), add packages to your project

View File

@@ -8,7 +8,8 @@ code compiles one-to-one into the equivalent JS, and there is no
interpretation at runtime.
CoffeeScript is supported on both the client and the server. Files
ending with `.coffee` are automatically compiled to JavaScript.
ending with `.coffee` or `.litcoffee` are automatically compiled to
JavaScript.
See <http://jashkenas.github.com/coffee-script/> for more information.

2
meteor
View File

@@ -1,6 +1,6 @@
#!/bin/bash
BUNDLE_VERSION=0.2.20
BUNDLE_VERSION=0.2.21
# OS Check. Put here because here is where we download the precompiled
# bundles that are arch specific.

View File

@@ -122,9 +122,19 @@
return user;
};
Accounts.insertUserDoc = function (options, user) {
// add created at timestamp (and protect passed in user object from
// modification)
user = _.extend({createdAt: +(new Date)}, user);
// - clone user document, to protect from modification
// - add createdAt timestamp
// - prepare an _id, so that you can modify other collections (eg
// create a first task for every new user)
//
// XXX If the onCreateUser or validateNewUser hooks fail, we might
// end up having modified some other collection
// inappropriately. The solution is probably to have onCreateUser
// accept two callbacks - one that gets called before inserting
// the user document (in which you can modify its contents), and
// one that gets called after (in which you should change other
// collections)
user = _.extend({createdAt: +(new Date), _id: Random.id()}, user);
var result = {};
if (options.generateLoginToken) {

View File

@@ -7,6 +7,16 @@ Tinytest.add('accounts - config validates keys', function (test) {
});
});
Tinytest.add('accounts - validateNewUser gets passed user with _id', function (test) {
var idInValidateNewUser;
Accounts.validateNewUser(function (user) {
idInValidateNewUser = user._id;
return true;
});
var newUserId = Accounts.updateOrCreateUserFromExternalService('foobook', {id: Random.id()}).id;
test.equal(idInValidateNewUser, newUserId);
});
Tinytest.add('accounts - updateOrCreateUserFromExternalService - Facebook', function (test) {
var facebookId = Random.id();

View File

@@ -1,4 +1,7 @@
Tinytest.add("coffeescript - presence", function(test) {
test.isTrue(Meteor.__COFFEESCRIPT_PRESENT);
});
Tinytest.add("literate coffeescript - presence", function(test) {
test.isTrue(Meteor.__LITCOFFEESCRIPT_PRESENT);
});

View File

@@ -0,0 +1,6 @@
This file is just the same as `coffeescript_tests.coffee`, first we set a
property, which we check for in `coffeescript_tests.js`, and then a trivial
testcase.
Meteor.__LITCOFFEESCRIPT_PRESENT = true
Tinytest.add "literate coffeescript - compile", (test) -> test.isTrue true

View File

@@ -4,30 +4,32 @@ Package.describe({
var coffee = require('coffee-script');
var fs = require('fs');
var path = require('path');
Package.register_extension(
"coffee", function (bundle, source_path, serve_path, where) {
serve_path = serve_path + '.js';
var coffeescript_handler = function(bundle, source_path, serve_path, where) {
serve_path = serve_path + '.js';
var contents = fs.readFileSync(source_path);
var options = {bare: true, filename: source_path};
try {
contents = coffee.compile(contents.toString('utf8'), options);
} catch (e) {
return bundle.error(e.message);
}
contents = new Buffer(contents);
bundle.add_resource({
type: "js",
path: serve_path,
data: contents,
where: where
});
var contents = fs.readFileSync(source_path);
var options = {bare: true, filename: source_path, literate: path.extname(source_path) === '.litcoffee'};
try {
contents = coffee.compile(contents.toString('utf8'), options);
} catch (e) {
return bundle.error(e.message);
}
);
contents = new Buffer(contents);
bundle.add_resource({
type: "js",
path: serve_path,
data: contents,
where: where
});
}
Package.register_extension("coffee", coffeescript_handler);
Package.register_extension("litcoffee", coffeescript_handler);
Package.on_test(function (api) {
api.add_files(['coffeescript_tests.coffee', 'coffeescript_tests.js'],
api.add_files(['coffeescript_tests.coffee', 'litcoffeescript_tests.litcoffee', 'coffeescript_tests.js'],
['client', 'server']);
});

View File

@@ -104,7 +104,10 @@ EJSON._isCustomType = function (obj) {
var adjustTypesToJSONValue =
EJSON._adjustTypesToJSONValue = function (obj) {
if (obj === null)
return;
return null;
var maybeChanged = toJSONValueHelper(obj);
if (maybeChanged !== undefined)
return maybeChanged;
_.each(obj, function (value, key) {
if (typeof value !== 'object' && value !== undefined)
return; // continue
@@ -117,6 +120,7 @@ EJSON._adjustTypesToJSONValue = function (obj) {
// at this level. recurse.
adjustTypesToJSONValue(value);
});
return obj;
};
// Either return the JSON-compatible version of the argument, or undefined (if
@@ -142,11 +146,16 @@ EJSON.toJSONValue = function (item) {
return item;
};
//for both arrays and objects
//for both arrays and objects. Tries its best to just
// use the object you hand it, but may return something
// different if the object you hand it itself needs changing.
var adjustTypesFromJSONValue =
EJSON._adjustTypesFromJSONValue = function (obj) {
if (obj === null)
return;
return null;
var maybeChanged = fromJSONValueHelper(obj);
if (maybeChanged !== obj)
return maybeChanged;
_.each(obj, function (value, key) {
if (typeof value === 'object') {
var changed = fromJSONValueHelper(value);
@@ -159,6 +168,7 @@ EJSON._adjustTypesFromJSONValue = function (obj) {
adjustTypesFromJSONValue(value);
}
});
return obj;
};
// Either return the argument changed to have the non-json
@@ -212,6 +222,8 @@ EJSON.equals = function (a, b, options) {
var keyOrderSensitive = !!(options && options.keyOrderSensitive);
if (a === b)
return true;
if (!a || !b) // if either one is falsy, they'd have to be === to be equal
return false;
if (!(typeof a === 'object' && typeof b === 'object'))
return false;
if (a instanceof Date && b instanceof Date)

View File

@@ -31,3 +31,12 @@ Tinytest.add("ejson - nesting and literal", function (test) {
var roundTrip = EJSON.fromJSONValue(eObj);
test.equal(obj, roundTrip);
});
Tinytest.add("ejson - equality and falsiness", function (test) {
test.isTrue(EJSON.equals(null, null));
test.isTrue(EJSON.equals(undefined, undefined));
test.isFalse(EJSON.equals({foo: "foo"}, null));
test.isFalse(EJSON.equals(null, {foo: "foo"}));
test.isFalse(EJSON.equals(undefined, {foo: "foo"}));
test.isFalse(EJSON.equals({foo: "foo"}, undefined));
});

View File

@@ -79,7 +79,7 @@ Meteor._parseDDP = function (stringMessage) {
_.each(['fields', 'params', 'result'], function (field) {
if (_.has(msg, field))
EJSON._adjustTypesFromJSONValue(msg[field]);
msg[field] = EJSON._adjustTypesFromJSONValue(msg[field]);
});
return msg;
@@ -105,7 +105,7 @@ Meteor._stringifyDDP = function (msg) {
// adjust types to basic
_.each(['fields', 'params', 'result'], function (field) {
if (_.has(copy, field))
EJSON._adjustTypesToJSONValue(copy[field]);
copy[field] = EJSON._adjustTypesToJSONValue(copy[field]);
});
if (msg.id && typeof msg.id !== 'string') {
throw new Error("Message id is not a string");

View File

@@ -5,6 +5,9 @@ Meteor.methods({
echo: function (/* arguments */) {
return _.toArray(arguments);
},
echoOne: function (/*arguments*/) {
return arguments[0];
},
exception: function (where, intended) {
var shouldThrow =
(Meteor.isServer && where === "server") ||

View File

@@ -57,12 +57,15 @@ Tinytest.add("livedata - methods with colliding names", function (test) {
var echoTest = function (item) {
return function (test, expect) {
if (Meteor.isServer)
if (Meteor.isServer) {
test.equal(Meteor.call("echo", item), [item]);
test.equal(Meteor.call("echoOne", item), item);
}
if (Meteor.isClient)
test.equal(Meteor.call("echo", item), undefined);
test.equal(Meteor.call("echo", item, expect(undefined, [item])), undefined);
test.equal(Meteor.call("echoOne", item, expect(undefined, item)), undefined);
};
};

View File

@@ -1,11 +1,5 @@
## CORE FUNCTIONALITY THAT'S MISSING ##
Dot notation is not supported at all in selectors. (Specifically: it's
implemented but not tested, and ordinal indexing (into lists) isn't
implemented at all.)
In selectors, $elemMatch is not implemented.
In update, $pull can't take a selector like {$gt: 3} (but it can take
{x: 3}, or {x: {$gt: 3}} -- basically, selectors that match documents
can be used, but selectors that are intended to match non-document
@@ -18,11 +12,9 @@ Sort does not support subkeys. You can sort on 'a', but not 'a.b'.
## ON TYPES ##
Only the basic JSON types are implemented (string, number, boolean,
null, array, object). These additional Mongo types aren't implemented,
or aren't implemented completely: object id, binary data, date,
timestamp, symbol, javascript code (with or without scope),
minkey/maxkey, regexp (stored in the database.)
We don't implement these Mongo types completely: timestamp (but date works),
symbol, javascript code (with or without scope), minkey/maxkey, regexp (stored
in the database), fixed-precision integers.
If your Javascript implementation enumerates the keys of objects in a
consistent order, then we implement object equality and object
@@ -37,25 +29,15 @@ integer type, and we don't support the integer type yet.)
## API ##
Our findLive() extension needs a full set of tests (it doesn't have
any yet.)
find() doesn't support retrieving a subset of fields. It always
returns the whole doc. findLive() doesn't support it either (it might
be nice, to limit what changes you'll receive notifications on.)
There's no such thing as a cursor. find() just returns the whole
result set.
returns the whole doc.
find() doesn't support the min and max parameters.
findAndModify isn't supported.
The aggregate functions count(), distinct(), and group() aren't
supported. Map/reduce isn't supported.
findLive() doesn't support any kind of pagination. You always get all
of the results.
The aggregate functions distinct(), and group() aren't supported. Map/reduce
isn't supported.
update() should have a clear stance on atomicity (both in terms of
multiple ops on a single document, and on multi-document update mode.)

View File

@@ -692,7 +692,6 @@ testAsyncMulti('mongo-livedata - document with a custom type, ' + idGeneration,
test.equal(cursor.count(), 1);
var inColl = coll.findOne();
test.isTrue(inColl);
debugger;
inColl && test.equal(inColl.d.speak(), "woof");
}));
}

View File

@@ -8,19 +8,14 @@ Package.describe({
// XXX hack -- need a way to use a package at bundle time
var _ = require(path.join('..', '..', 'packages', 'underscore', 'underscore.js'));
Package.on_use(function (api, where) {
where = where || ['client', 'server'];
api.use(['underscore', 'deps'], where);
Package.on_use(function (api) {
api.use(['underscore', 'deps'], 'client');
// XXX what I really want to do is ensure that if 'reload' is going to
// be loaded, it should be loaded before 'session'. Session can work
// with or without reload.
if (where === "client" ||
(where instanceof Array && _.indexOf(where, "client") !== -1)) {
api.use("reload", "client");
}
api.use('reload', 'client');
api.add_files('session.js', where);
api.add_files('session.js', 'client');
});
Package.on_test(function (api) {

View File

@@ -9,7 +9,7 @@
// not obey the _escaped_fragment_ protocol. The page is served
// statically to any client whos user agent matches any of these
// regexps. (possibly make this list configurable by user).
var AGENTS = [/^facebookexternalhit/];
var AGENTS = [/^facebookexternalhit/i, /^linkedinbot/i];
// how long to let phantomjs run before we kill it
var REQUEST_TIMEOUT = 15*1000;