Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

We got the method signature wrong the first time around. Meteor optio…

…ns and Knockout Mapping options are now separate arguments to find() and findOne().
  • Loading branch information...
commit 62ebc440a90edcdbbe6939491849a574237aeab9 1 parent fecbcbc
Steven Luscher authored
20 README.md
View
@@ -21,7 +21,7 @@ Use `ko.meteor.find()` and `ko.meteor.findOne()` like you would normally use `ko
var viewModel = {
unfinishedTodos: ko.meteor.find(Todos, {done: false}),
finishedTodos: ko.meteor.find(Todos, {done: true}),
- oldestUnfinishedTodo: ko.meteor.findOne(Todos, {done: true}, {meteor_options: {sort: {created_at:1}}})
+ oldestUnfinishedTodo: ko.meteor.findOne(Todos, {done: true}, {sort: {created_at:1}})
};
Meteor.startup( function() { ko.applyBindings(viewModel); } );
@@ -39,8 +39,8 @@ Any update to the Meteor `Todos` collection will now trigger a UI refresh. This
`ko.meteor.find()` and `ko.meteor.findOne()` share the same method signature.
- ko.meteor.find( collection, selector[, options] )
- ko.meteor.findOne( collection, selector[, options] )
+ ko.meteor.find( collection, selector[, options, mapping] )
+ ko.meteor.findOne( collection, selector[, options, mapping] )
### The `collection` argument ###
@@ -52,19 +52,17 @@ A Mongo selector, a String, or an `Observable` that wraps a Mongo selector or St
### The `options` argument ###
-(Optional) An Object, or an `Observable` that wraps an Object. Recognizes the following keys:
+(Optional) An Object, or an `Observable` that wraps an Object. See the Meteor documentation on the `options` argument of [`find()`](http://docs.meteor.com/#find) and [`findOne()`](http://docs.meteor.com/#findone) for more information.
-* `view_model` – an object constructor.
+### The `mapping` argument ###
-> The mapper will instantiate an object using this constructor, then map each record in the Meteor Collection to the resulting instance. The constructor will receive, as its first parameter, an object representing the data returned from the query.
+(Optional) An Object, or an `Observable` that wraps an Object. Recognizes the following special property:
-* `meteor_options`additional configuration for `Meteor.Collection.find()` or `Meteor.Collection.findOne()`.
+* `view_model`an object constructor.
-> See the Meteor documentation on the `options` argument of [`find()`](http://docs.meteor.com/#find) and [`findOne()`](http://docs.meteor.com/#findone) for more information.
-
-* `mapping` – additional configuration for the Knockout Mapping plugin.
+> The mapper will instantiate an object using this constructor, then map each record in the Meteor Collection to the resulting instance. The constructor will receive, as its first parameter, an object representing the data returned from the query.
-> See the "[Advanced Usage](http://knockoutjs.com/documentation/plugins-mapping.html#advanced_usage)" section of the Knockout Mapping documentation for more information.
+The remaining `mapping` properties will be passed through to the Knockout Mapping plugin. See the "[Advanced Usage](http://knockoutjs.com/documentation/plugins-mapping.html#advanced_usage)" section of the Knockout Mapping documentation for more information.
## Requirements ##
63 build/knockout.meteor.js
View
@@ -33,27 +33,34 @@ http://github.com/steveluscher/knockout.meteor
})(Error);
meteor = {
- find: function(collection, selector, options) {
+ find: function(collection, selector, options, mapping) {
if (options == null) {
options = {};
}
- return (new FindMany(collection, selector, options)).run();
+ if (mapping == null) {
+ mapping = {};
+ }
+ return (new FindMany(collection, selector, options, mapping)).run();
},
- findOne: function(collection, selector, options) {
+ findOne: function(collection, selector, options, mapping) {
if (options == null) {
options = {};
}
- return (new FindOne(collection, selector, options)).run();
+ if (mapping == null) {
+ mapping = {};
+ }
+ return (new FindOne(collection, selector, options, mapping)).run();
}
};
AbstractFinder = (function() {
- function AbstractFinder(collection, selector, options) {
+ function AbstractFinder(collection, selector, options, mapping) {
var _this = this;
this.collection = collection;
this.selector = selector;
this.options = options != null ? options : {};
+ this.mapping = mapping != null ? mapping : {};
this.run = __bind(this.run, this);
this.target = null;
@@ -61,50 +68,50 @@ http://github.com/steveluscher/knockout.meteor
ko.utils.unwrapObservable(_this.collection);
ko.utils.unwrapObservable(_this.selector);
ko.utils.unwrapObservable(_this.options);
+ ko.utils.unwrapObservable(_this.mapping);
}).extend({
throttle: 1
}).subscribe(this.run);
}
AbstractFinder.prototype.run = function() {
- var collection, options, selector;
+ var collection, mapping, options, selector;
if (this.query) {
this.query.destroy();
}
collection = ko.utils.unwrapObservable(this.collection);
selector = ko.utils.unwrapObservable(this.selector);
options = ko.utils.unwrapObservable(this.options);
- this.applyDefaults(options);
- this.query = this.createQuery(collection, selector, options);
+ mapping = this.processMapping(ko.utils.unwrapObservable(this.mapping));
+ this.query = this.createQuery(collection, selector, options, mapping);
return this.query.run();
};
- AbstractFinder.prototype.applyDefaults = function(options) {
- _.defaults(options, {
- mapping: {},
- view_model: null
- });
- if (!_.isObject(options.mapping[""])) {
- options.mapping[""] = {};
+ AbstractFinder.prototype.processMapping = function(raw_mapping) {
+ var mapping, view_model;
+ mapping = _.clone(raw_mapping);
+ view_model = mapping.view_model;
+ delete mapping.view_model;
+ if (!_.isObject(mapping[""])) {
+ mapping[""] = {};
}
- _.defaults(options.mapping[""], {
+ _.defaults(mapping[""], {
key: function(item) {
return ko.utils.unwrapObservable(item._id);
}
});
- if (_.isFunction(options.view_model)) {
- return options.mapping[""].create = function(opts) {
- var view_model;
+ if (_.isFunction(view_model)) {
+ mapping[""].create = function(opts) {
if (!opts.data) {
return ko.observable();
}
- view_model = new options.view_model(opts.data);
- return ko.mapping.fromJS(opts.data, options.mapping, view_model);
+ return ko.mapping.fromJS(opts.data, mapping, new view_model(opts.data));
};
}
+ return mapping;
};
- AbstractFinder.prototype.createQuery = function(collection, selector, options) {
+ AbstractFinder.prototype.createQuery = function(collection, selector, options, mapping) {
throw new NotImplementedError('createQuery');
};
@@ -120,14 +127,14 @@ http://github.com/steveluscher/knockout.meteor
return FindMany.__super__.constructor.apply(this, arguments);
}
- FindMany.prototype.createQuery = function(collection, selector, options) {
+ FindMany.prototype.createQuery = function(collection, selector, options, mapping) {
var data_func, meteor_cursor;
- meteor_cursor = collection.find(selector, options.meteor_options);
+ meteor_cursor = collection.find(selector, options);
data_func = function() {
meteor_cursor.rewind();
return meteor_cursor.fetch();
};
- return new MappedQuery(this, data_func, options.mapping);
+ return new MappedQuery(this, data_func, mapping);
};
return FindMany;
@@ -142,12 +149,12 @@ http://github.com/steveluscher/knockout.meteor
return FindOne.__super__.constructor.apply(this, arguments);
}
- FindOne.prototype.createQuery = function(collection, selector, options) {
+ FindOne.prototype.createQuery = function(collection, selector, options, mapping) {
var data_func;
data_func = function() {
- return collection.findOne(selector, options.meteor_options);
+ return collection.findOne(selector, options);
};
- return new MappedQuery(this, data_func, options.mapping);
+ return new MappedQuery(this, data_func, mapping);
};
return FindOne;
63 examples/dynamic_finders/client/knockout.meteor.js
View
@@ -33,27 +33,34 @@ http://github.com/steveluscher/knockout.meteor
})(Error);
meteor = {
- find: function(collection, selector, options) {
+ find: function(collection, selector, options, mapping) {
if (options == null) {
options = {};
}
- return (new FindMany(collection, selector, options)).run();
+ if (mapping == null) {
+ mapping = {};
+ }
+ return (new FindMany(collection, selector, options, mapping)).run();
},
- findOne: function(collection, selector, options) {
+ findOne: function(collection, selector, options, mapping) {
if (options == null) {
options = {};
}
- return (new FindOne(collection, selector, options)).run();
+ if (mapping == null) {
+ mapping = {};
+ }
+ return (new FindOne(collection, selector, options, mapping)).run();
}
};
AbstractFinder = (function() {
- function AbstractFinder(collection, selector, options) {
+ function AbstractFinder(collection, selector, options, mapping) {
var _this = this;
this.collection = collection;
this.selector = selector;
this.options = options != null ? options : {};
+ this.mapping = mapping != null ? mapping : {};
this.run = __bind(this.run, this);
this.target = null;
@@ -61,50 +68,50 @@ http://github.com/steveluscher/knockout.meteor
ko.utils.unwrapObservable(_this.collection);
ko.utils.unwrapObservable(_this.selector);
ko.utils.unwrapObservable(_this.options);
+ ko.utils.unwrapObservable(_this.mapping);
}).extend({
throttle: 1
}).subscribe(this.run);
}
AbstractFinder.prototype.run = function() {
- var collection, options, selector;
+ var collection, mapping, options, selector;
if (this.query) {
this.query.destroy();
}
collection = ko.utils.unwrapObservable(this.collection);
selector = ko.utils.unwrapObservable(this.selector);
options = ko.utils.unwrapObservable(this.options);
- this.applyDefaults(options);
- this.query = this.createQuery(collection, selector, options);
+ mapping = this.processMapping(ko.utils.unwrapObservable(this.mapping));
+ this.query = this.createQuery(collection, selector, options, mapping);
return this.query.run();
};
- AbstractFinder.prototype.applyDefaults = function(options) {
- _.defaults(options, {
- mapping: {},
- view_model: null
- });
- if (!_.isObject(options.mapping[""])) {
- options.mapping[""] = {};
+ AbstractFinder.prototype.processMapping = function(raw_mapping) {
+ var mapping, view_model;
+ mapping = _.clone(raw_mapping);
+ view_model = mapping.view_model;
+ delete mapping.view_model;
+ if (!_.isObject(mapping[""])) {
+ mapping[""] = {};
}
- _.defaults(options.mapping[""], {
+ _.defaults(mapping[""], {
key: function(item) {
return ko.utils.unwrapObservable(item._id);
}
});
- if (_.isFunction(options.view_model)) {
- return options.mapping[""].create = function(opts) {
- var view_model;
+ if (_.isFunction(view_model)) {
+ mapping[""].create = function(opts) {
if (!opts.data) {
return ko.observable();
}
- view_model = new options.view_model(opts.data);
- return ko.mapping.fromJS(opts.data, options.mapping, view_model);
+ return ko.mapping.fromJS(opts.data, mapping, new view_model(opts.data));
};
}
+ return mapping;
};
- AbstractFinder.prototype.createQuery = function(collection, selector, options) {
+ AbstractFinder.prototype.createQuery = function(collection, selector, options, mapping) {
throw new NotImplementedError('createQuery');
};
@@ -120,14 +127,14 @@ http://github.com/steveluscher/knockout.meteor
return FindMany.__super__.constructor.apply(this, arguments);
}
- FindMany.prototype.createQuery = function(collection, selector, options) {
+ FindMany.prototype.createQuery = function(collection, selector, options, mapping) {
var data_func, meteor_cursor;
- meteor_cursor = collection.find(selector, options.meteor_options);
+ meteor_cursor = collection.find(selector, options);
data_func = function() {
meteor_cursor.rewind();
return meteor_cursor.fetch();
};
- return new MappedQuery(this, data_func, options.mapping);
+ return new MappedQuery(this, data_func, mapping);
};
return FindMany;
@@ -142,12 +149,12 @@ http://github.com/steveluscher/knockout.meteor
return FindOne.__super__.constructor.apply(this, arguments);
}
- FindOne.prototype.createQuery = function(collection, selector, options) {
+ FindOne.prototype.createQuery = function(collection, selector, options, mapping) {
var data_func;
data_func = function() {
- return collection.findOne(selector, options.meteor_options);
+ return collection.findOne(selector, options);
};
- return new MappedQuery(this, data_func, options.mapping);
+ return new MappedQuery(this, data_func, mapping);
};
return FindOne;
6 examples/dynamic_finders/example.js
View
@@ -17,9 +17,9 @@ if (Meteor.is_client) {
});
var options = ko.computed(function() {
- var meteor_options = { sort: {} };
- meteor_options.sort[sortField().toLowerCase()] = sortAsc() ? 1 : -1;
- return { meteor_options: meteor_options };
+ var options = { sort: {} };
+ options.sort[sortField().toLowerCase()] = sortAsc() ? 1 : -1;
+ return options;
});
// finders!
63 examples/todo_list/client/knockout.meteor.js
View
@@ -33,27 +33,34 @@ http://github.com/steveluscher/knockout.meteor
})(Error);
meteor = {
- find: function(collection, selector, options) {
+ find: function(collection, selector, options, mapping) {
if (options == null) {
options = {};
}
- return (new FindMany(collection, selector, options)).run();
+ if (mapping == null) {
+ mapping = {};
+ }
+ return (new FindMany(collection, selector, options, mapping)).run();
},
- findOne: function(collection, selector, options) {
+ findOne: function(collection, selector, options, mapping) {
if (options == null) {
options = {};
}
- return (new FindOne(collection, selector, options)).run();
+ if (mapping == null) {
+ mapping = {};
+ }
+ return (new FindOne(collection, selector, options, mapping)).run();
}
};
AbstractFinder = (function() {
- function AbstractFinder(collection, selector, options) {
+ function AbstractFinder(collection, selector, options, mapping) {
var _this = this;
this.collection = collection;
this.selector = selector;
this.options = options != null ? options : {};
+ this.mapping = mapping != null ? mapping : {};
this.run = __bind(this.run, this);
this.target = null;
@@ -61,50 +68,50 @@ http://github.com/steveluscher/knockout.meteor
ko.utils.unwrapObservable(_this.collection);
ko.utils.unwrapObservable(_this.selector);
ko.utils.unwrapObservable(_this.options);
+ ko.utils.unwrapObservable(_this.mapping);
}).extend({
throttle: 1
}).subscribe(this.run);
}
AbstractFinder.prototype.run = function() {
- var collection, options, selector;
+ var collection, mapping, options, selector;
if (this.query) {
this.query.destroy();
}
collection = ko.utils.unwrapObservable(this.collection);
selector = ko.utils.unwrapObservable(this.selector);
options = ko.utils.unwrapObservable(this.options);
- this.applyDefaults(options);
- this.query = this.createQuery(collection, selector, options);
+ mapping = this.processMapping(ko.utils.unwrapObservable(this.mapping));
+ this.query = this.createQuery(collection, selector, options, mapping);
return this.query.run();
};
- AbstractFinder.prototype.applyDefaults = function(options) {
- _.defaults(options, {
- mapping: {},
- view_model: null
- });
- if (!_.isObject(options.mapping[""])) {
- options.mapping[""] = {};
+ AbstractFinder.prototype.processMapping = function(raw_mapping) {
+ var mapping, view_model;
+ mapping = _.clone(raw_mapping);
+ view_model = mapping.view_model;
+ delete mapping.view_model;
+ if (!_.isObject(mapping[""])) {
+ mapping[""] = {};
}
- _.defaults(options.mapping[""], {
+ _.defaults(mapping[""], {
key: function(item) {
return ko.utils.unwrapObservable(item._id);
}
});
- if (_.isFunction(options.view_model)) {
- return options.mapping[""].create = function(opts) {
- var view_model;
+ if (_.isFunction(view_model)) {
+ mapping[""].create = function(opts) {
if (!opts.data) {
return ko.observable();
}
- view_model = new options.view_model(opts.data);
- return ko.mapping.fromJS(opts.data, options.mapping, view_model);
+ return ko.mapping.fromJS(opts.data, mapping, new view_model(opts.data));
};
}
+ return mapping;
};
- AbstractFinder.prototype.createQuery = function(collection, selector, options) {
+ AbstractFinder.prototype.createQuery = function(collection, selector, options, mapping) {
throw new NotImplementedError('createQuery');
};
@@ -120,14 +127,14 @@ http://github.com/steveluscher/knockout.meteor
return FindMany.__super__.constructor.apply(this, arguments);
}
- FindMany.prototype.createQuery = function(collection, selector, options) {
+ FindMany.prototype.createQuery = function(collection, selector, options, mapping) {
var data_func, meteor_cursor;
- meteor_cursor = collection.find(selector, options.meteor_options);
+ meteor_cursor = collection.find(selector, options);
data_func = function() {
meteor_cursor.rewind();
return meteor_cursor.fetch();
};
- return new MappedQuery(this, data_func, options.mapping);
+ return new MappedQuery(this, data_func, mapping);
};
return FindMany;
@@ -142,12 +149,12 @@ http://github.com/steveluscher/knockout.meteor
return FindOne.__super__.constructor.apply(this, arguments);
}
- FindOne.prototype.createQuery = function(collection, selector, options) {
+ FindOne.prototype.createQuery = function(collection, selector, options, mapping) {
var data_func;
data_func = function() {
- return collection.findOne(selector, options.meteor_options);
+ return collection.findOne(selector, options);
};
- return new MappedQuery(this, data_func, options.mapping);
+ return new MappedQuery(this, data_func, mapping);
};
return FindOne;
12 examples/todo_list/example.js
View
@@ -40,22 +40,22 @@ if (Meteor.is_client) {
unfinishedTodos: ko.meteor.find(
Todos,
{done: false},
- {mapping: todoMapping}
+ {},
+ todoMapping
),
// Todos where 'done' == true
finishedTodos: ko.meteor.find(
Todos,
{done: true},
- {mapping: todoMapping}
+ {},
+ todoMapping
),
// The todo with the oldest 'created_at' where 'done' == false
oldestUnfinishedTodo: ko.meteor.findOne(
Todos,
{done: false},
- {
- meteor_options: {sort: {created_at: 1}},
- mapping: todoMapping
- }
+ {sort: {created_at: 1}},
+ todoMapping
)
};
54 src/knockout.meteor.coffee
View
@@ -19,11 +19,11 @@ class NotImplementedError extends Error
# These functions are exported as ko.meteor.find and ko.meteor.findOne
#
meteor =
- find: (collection, selector, options = {}) ->
- (new FindMany(collection, selector, options)).run()
+ find: (collection, selector, options = {}, mapping = {}) ->
+ (new FindMany(collection, selector, options, mapping)).run()
- findOne: (collection, selector, options = {}) ->
- (new FindOne(collection, selector, options)).run()
+ findOne: (collection, selector, options = {}, mapping = {}) ->
+ (new FindOne(collection, selector, options, mapping)).run()
#
# A Finder accepts a collection, selector, and options hash as arguments,
@@ -38,7 +38,7 @@ meteor =
# options - an Object, or a Knockout observable that returns an Object
#
class AbstractFinder
- constructor: (@collection, @selector, @options = {}) ->
+ constructor: (@collection, @selector, @options = {}, @mapping = {}) ->
@target = null
# If an argument to this finder happens to be a Knockout observable,
@@ -47,6 +47,7 @@ class AbstractFinder
ko.utils.unwrapObservable(@collection)
ko.utils.unwrapObservable(@selector)
ko.utils.unwrapObservable(@options)
+ ko.utils.unwrapObservable(@mapping)
return
.extend({throttle: 1}) # Defer, in case more than one argument changes at a time
.subscribe(@run) # Run every time changes are detected
@@ -59,24 +60,26 @@ class AbstractFinder
collection = ko.utils.unwrapObservable(@collection)
selector = ko.utils.unwrapObservable(@selector)
options = ko.utils.unwrapObservable(@options)
- @applyDefaults(options)
+ mapping = @processMapping(ko.utils.unwrapObservable(@mapping))
# Create a MappedQuery (as defined in subclass)
- @query = @createQuery(collection, selector, options)
+ @query = @createQuery(collection, selector, options, mapping)
# Run the query
@query.run()
- applyDefaults: (options) ->
- _.defaults options,
- mapping: {}
- view_model: null
+ processMapping: (raw_mapping) ->
+ mapping = _.clone(raw_mapping)
+
+ # Extract the view_model property
+ view_model = mapping.view_model
+ delete mapping.view_model
# If a root level mapping doesn't exist, create it
- options.mapping[""] = {} unless _.isObject(options.mapping[""])
+ mapping[""] = {} unless _.isObject(mapping[""])
# Merge in some mapping defaults
- _.defaults options.mapping[""],
+ _.defaults mapping[""],
# It's important to key collection members by their Mongo _id so that
# the Knockout Mapping plugin can determine if an object is new or old
key: (item) -> ko.utils.unwrapObservable(item._id)
@@ -84,34 +87,35 @@ class AbstractFinder
# If we were passed a view_model in the options hash,
# instruct the Knockout Mapping plugin to instantiate
# each Meteor record as an instance of that model
- if _.isFunction options.view_model
- options.mapping[""].create = (opts) ->
+ if _.isFunction view_model
+ mapping[""].create = (opts) ->
return ko.observable() unless opts.data
- view_model = new options.view_model(opts.data)
- ko.mapping.fromJS(opts.data, options.mapping, view_model)
-
- createQuery: (collection, selector, options) ->
+ ko.mapping.fromJS(opts.data, mapping, new view_model(opts.data))
+
+ mapping
+
+ createQuery: (collection, selector, options, mapping) ->
throw new NotImplementedError('createQuery')
class FindMany extends AbstractFinder
- createQuery: (collection, selector, options) ->
+ createQuery: (collection, selector, options, mapping) ->
# Set up the Meteor cursor for this selector
- meteor_cursor = collection.find(selector, options.meteor_options)
+ meteor_cursor = collection.find(selector, options)
# This is the function we want rerun when the result of this query changes
data_func = ->
meteor_cursor.rewind()
meteor_cursor.fetch()
- new MappedQuery(@, data_func, options.mapping)
+ new MappedQuery(@, data_func, mapping)
class FindOne extends AbstractFinder
- createQuery: (collection, selector, options) ->
+ createQuery: (collection, selector, options, mapping) ->
# This is the function we want rerun when the result of this query changes
- data_func = -> collection.findOne(selector, options.meteor_options)
+ data_func = -> collection.findOne(selector, options)
- new MappedQuery(@, data_func, options.mapping)
+ new MappedQuery(@, data_func, mapping)
#
# A MappedQuery monitors a finder for changes in its dataset. When it detects
Please sign in to comment.
Something went wrong with that request. Please try again.