mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'devel' into deps-radical
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
2
meteor
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
6
packages/coffeescript/litcoffeescript_tests.litcoffee
Normal file
6
packages/coffeescript/litcoffeescript_tests.litcoffee
Normal 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
|
||||
@@ -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']);
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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") ||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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.)
|
||||
|
||||
@@ -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");
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user