Code Monkey home page Code Monkey logo

meteor-collection-hooks's Introduction

Meteor Collection Hooks

Test suite Code lint CodeQL Analysis

Extends Mongo.Collection with before/after hooks for insert, update, remove, find, and findOne.

Works across client, server or a mix. Also works when a client initiates a collection method and the server runs the hook, all while respecting the collection validators (allow/deny).

Please refer to History.md for a summary of recent changes.

Getting Started

Installation:

meteor add matb33:collection-hooks

.before.insert(userId, doc)

Fired before the doc is inserted.

Allows you to modify doc as needed, or run additional functionality

  • this.transform() obtains transformed version of document, if a transform was defined.
import { Mongo } from 'meteor/mongo';
const test = new Mongo.Collection("test");

test.before.insert(function (userId, doc) {
  doc.createdAt = Date.now();
});

.before.update(userId, doc, fieldNames, modifier, options)

Fired before the doc is updated.

Allows you to to change the modifier as needed, or run additional functionality.

  • this.transform() obtains transformed version of document, if a transform was defined.
test.before.update(function (userId, doc, fieldNames, modifier, options) {
  modifier.$set = modifier.$set || {};
  modifier.$set.modifiedAt = Date.now();
});

Important:

  1. Note that we are changing modifier, and not doc. Changing doc won't have any effect as the document is a copy and is not what ultimately gets sent down to the underlying update method.

  2. When triggering a single update targeting multiple documents using the option multi: true (see Meteor documentation), the before.update hook is called once per document about to be updated, but the collection update called afterwards remains a single update (targetting multiple documents) with a single modifier. Hence it is not possible at the time to use before.update to create a specific modifier for each targeted document.


.before.remove(userId, doc)

Fired just before the doc is removed.

Allows you to to affect your system while the document is still in existence -- useful for maintaining system integrity, such as cascading deletes.

  • this.transform() obtains transformed version of document, if a transform was defined.
test.before.remove(function (userId, doc) {
  // ...
});

.before.upsert(userId, selector, modifier, options)

Fired before the doc is upserted.

Allows you to to change the modifier as needed, or run additional functionality.

test.before.upsert(function (userId, selector, modifier, options) {
  modifier.$set = modifier.$set || {};
  modifier.$set.modifiedAt = Date.now();
});

Note that calling upsert will always fire .before.upsert hooks, but will call either .after.insert or .after.update hooks depending on the outcome of the upsert operation. There is no such thing as a .after.upsert hook at this time.


.after.insert(userId, doc)

Fired after the doc was inserted.

Allows you to run post-insert tasks, such as sending notifications of new document insertions.

  • this.transform() obtains transformed version of document, if a transform was defined;
  • this._id holds the newly inserted _id if available.
test.after.insert(function (userId, doc) {
  // ...
});

.after.update(userId, doc, fieldNames, modifier, options)

Fired after the doc was updated.

Allows you to to run post-update tasks, potentially comparing the previous and new documents to take further action.

  • this.previous contains the document before it was updated.
    • The optional fetchPrevious option, when set to false, will not fetch documents before running the hooks. this.previous will then not be available. The default behavior is to fetch the documents.
  • this.transform() obtains transformed version of document, if a transform was defined. Note that this function accepts an optional parameter to specify the document to transform — useful to transform previous: this.transform(this.previous).
test.after.update(function (userId, doc, fieldNames, modifier, options) {
  // ...
}, {fetchPrevious: true/false});

Important: If you have multiple hooks defined, and at least one of them does not specify fetchPrevious: false, then the documents will be fetched and provided as this.previous to all hook callbacks. All after-update hooks for the same collection must have fetchPrevious: false set in order to effectively disable the pre-fetching of documents.

It is instead recommended to use the collection-wide options (e.g. MyCollection.hookOptions.after.update = {fetchPrevious: false};).

This hook will always be called with the new documents; even if the updated document gets modified in a way were it would normally not be able to be found because of before.find hooks (see #297).


.after.remove(userId, doc)

Fired after the doc was removed.

doc contains a copy of the document before it was removed.

Allows you to run post-removal tasks that don't necessarily depend on the document being found in the database (external service clean-up for instance).

  • this.transform() obtains transformed version of document, if a transform was defined.
test.after.remove(function (userId, doc) {
  // ...
});

.before.find(userId, selector, options)

Fired before a find query.

Allows you to adjust selector/options on-the-fly.

test.before.find(function (userId, selector, options) {
  // ...
});

Important: This hook does not get called for after.update hooks (see #297).


.after.find(userId, selector, options, cursor)

Fired after a find query.

Allows you to act on a given find query. The cursor resulting from the query is provided as the last argument for convenience.

test.after.find(function (userId, selector, options, cursor) {
  // ...
});

.before.findOne(userId, selector, options)

Fired before a findOne query.

Allows you to adjust selector/options on-the-fly.

test.before.findOne(function (userId, selector, options) {
  // ...
});

.after.findOne(userId, selector, options, doc)

Fired after a findOne query.

Allows you to act on a given findOne query. The document resulting from the query is provided as the last argument for convenience.

test.after.findOne(function (userId, selector, options, doc) {
  // ...
});

Direct access (circumventing hooks)

All compatible methods have a direct version that circumvent any defined hooks. For example:

collection.direct.insert({_id: "test", test: 1});
collection.direct.insertAsync({_id: "test", test: 1});
collection.direct.upsert({_id: "test", test: 1});
collection.direct.upsertAsync({_id: "test", test: 1});
collection.direct.update({_id: "test"}, {$set: {test: 1}});
collection.direct.updateAsync({_id: "test"}, {$set: {test: 1}});
collection.direct.find({test: 1});
collection.direct.findOne({test: 1});
collection.direct.findOneAsync({test: 1});
collection.direct.remove({_id: "test"});
collection.direct.removeAsync({_id: "test"});

Default options

As of version 0.7.0, options can be passed to hook definitions. Default options can be specified globally and on a per-collection basis for all or some hooks, with more specific ones having higher specificity.

Examples (in order of least specific to most specific):

import { CollectionHooks } from 'meteor/matb33:collection-hooks';

CollectionHooks.defaults.all.all = {exampleOption: 1};

CollectionHooks.defaults.before.all = {exampleOption: 2};
CollectionHooks.defaults.after.all = {exampleOption: 3};

CollectionHooks.defaults.all.update = {exampleOption: 4};
CollectionHooks.defaults.all.remove = {exampleOption: 5};

CollectionHooks.defaults.before.insert = {exampleOption: 6};
CollectionHooks.defaults.after.remove = {exampleOption: 7};

Similarly, collection-wide options can be defined (these have a higher specificity than the global defaults from above):

import { Mongo } from 'meteor/mongo';
const testCollection = new Mongo.Collection("test");

testCollection.hookOptions.all.all = {exampleOption: 1};

testCollection.hookOptions.before.all = {exampleOption: 2};
testCollection.hookOptions.after.all = {exampleOption: 3};

testCollection.hookOptions.all.update = {exampleOption: 4};
testCollection.hookOptions.all.remove = {exampleOption: 5};

testCollection.hookOptions.before.insert = {exampleOption: 6};
testCollection.hookOptions.after.remove = {exampleOption: 7};

Currently (as of 0.7.0), only fetchPrevious is implemented as an option, and is only relevant to after-update hooks.


Additional notes

  • Returning false in any before hook will prevent the underlying method (and subsequent after hooks) from executing. Note that all before hooks will still continue to run even if the first hook returns false.

  • If you wish to make userId available to a find query in a publish function, try the technique detailed in this comment userId is available to find and findOne queries that were invoked within a publish function.

  • All hook callbacks have this._super available to them (the underlying method) as well as this.context, the equivalent of this to the underlying method. Additionally, this.args contain the original arguments passed to the method and can be modified by reference (for example, modifying a selector in a before hook so that the underlying method uses this new selector).

  • It is quite normal for userId to sometimes be unavailable to hook callbacks in some circumstances. For example, if an update is fired from the server with no user context, the server certainly won't be able to provide any particular userId.

  • You can define a defaultUserId in case you want to pass an userId to the hooks but there is no context. For instance if you are executing and API endpoint where the userId is derived from a token. Just assign the userId to CollectionHooks.defaultUserId. It will be overriden by the userId of the context if it exists.

  • If, like me, you transform Meteor.users through a round-about way involving find and findOne, then you won't be able to use this.transform(). Instead, grab the transformed user with findOne.

  • When adding a hook, a handler object is returned with these methods:

    • remove(): will remove that particular hook;
    • replace(callback, options): will replace the hook callback and options.
  • If your hook is defined in common code (both server and client), it will run twice: once on the server and once on the client. If your intention is for the hook to run only once, make sure the hook is defined somewhere where only either the client or the server reads it. When in doubt, define your hooks on the server.

  • Both update and remove internally make use of find, so be aware that find/findOne hooks can fire for those methods.

  • find hooks are also fired when fetching documents for update, upsert and remove hooks.

  • If using the direct version to bypass a hook, any mongo operations done within nested callbacks of the direct operation will also by default run as direct. You can use the following line in a nested callback before the operation to unset the direct setting: CollectionHooks.directEnv = new Meteor.EnvironmentVariable(false)


Maintainers

Maintained by Meteor Community Packages and in particular by:

Contributors

meteor-collection-hooks's People

Contributors

alisnic avatar aramk avatar copleykj avatar cpravetz avatar dependabot[bot] avatar evolross avatar jankapunkt avatar jdgjsag67251 avatar jonjamz avatar koenlav avatar manzato avatar matb33 avatar mbanting avatar mizzao avatar namirsab avatar nate-strauser avatar olragon avatar pierreozoux avatar rclai avatar repjackson avatar sakulstra avatar sebakerckhof avatar simonsimcity avatar storytellercz avatar subhog avatar tmeasday avatar vparpoil avatar wizonesolutions avatar zereraz avatar zimme avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

meteor-collection-hooks's Issues

Accounts Testing included everywhere (!)

Hey there,

I might be missing something, but I believe the accounts-testing package is making the Meteor.insecureUserLogin globally available in my Meteor app.

The reason for this is:

  1. Meteorite installs any package in smart.json into packages/
  2. Meteor automatically .use()s any package in packages/.

Point 2. will be addressed in the upcoming 0.6.5 release, but in the meantime (and for sometime afterwards), I would suggest that you should hold off on including those tests.

It's annoying I know.

Modifying the selector for find/findOne doesn't work as expected on client

The problem I'm wanting to solve: I have a collection of orders. Each order has a native _id and a customized orderId (something like S3-9446). I want to prevent other developers from using the wrong selector (e.g. {_id: 'S3-9446'} and {orderId: 's4LP7H6P5DAXrFLwo'} are both wrong). I do this by pre-checking the selector and modify it if necessary: http://screencast.com/t/6lGnvQjLy7tK

As you can see in the screencast, although the selector is correctly edited, the result is still undefined. On server the code works normally and always return the desired object.

Note: In the screencast, App.isWREId will return true if the argument is a customized id (like S3-9446).

Transforming without _id won't work with Blaze/UI/Shark

See Meteor-Community-Packages/Meteor-CollectionFS#146.

The upshot is that the new rendering engine requires that a doc being transformed must already have an _id property. It seems as though calling this.transform() within a before.insert causes an error because before.insert happens before the _id has been generated.

I think the only solutions would be to generate the _id property within this package prior to before.insert, or to remove the this.transform ability.

will Collection.before 'update' abort Collection update ?

Organizations.before 'update', (userId, options) ->
    options.updatedAt = new Date()
    options.timestamp = Date.now()

Meteor.methods

    editOrganization: (options, organizationId) ->
        if isAdmin() and typeof options is 'object' and organizationId
            Organizations.update _id: organizationId,
            $set: options

this is my code.

but update method didn't update document at all

Collection hooks success / error

Hey there,

I was just experimenting with this package and I tried something like...

Posts.after.insert(function(userId, doc){
  console.log(doc);
});

It appears that the hook is still called even if my Posts insert failed (due to allow / deny rules). Is there a way to only run the hooks on insert success? Or optionally run something else if there is an error?

0.9.2 issues

Collection Hooks has a bit of trouble with 0.9.2.

One change that may have caused it is the renaming of Meteor.Collection => Mongo.Collection

TypeError: Cannot call method 'update' of undefined
W20140916-14:20:41.359(3)? (STDERR)     at app/server/collection-hooks.js:1:54
W20140916-14:20:41.359(3)? (STDERR)     at app/server/collection-hooks.js:7:3

History/Changelog

Hi @matb33,

I started a changelog in a8fdfef. I use this Markdown format in all my other packages, in the same way that Meteor does it.

The idea is not to include any documentation or minor fixes in the changelog, only changes that are bug fixes or major differences in the way the code behaves. This makes it easier for people to see what might have happened if they updated to a new version and things don't work as expected. It was always missing for this package :)

I already went through the commit history and added the relevant things for the last two versions (covering 3 months), both as an example for you and so others can see changes in the recent versions. I typically add relevant changes in as I commit or merge pull requests. Hopefully you're on board with this and can also update the history as you add things going forward. This note is just so you know that it's there.

Cheers,
Andrew

Common context between hooks of same request

Is there currently a shared scope where hooks for the same request can share variables?

For example, if several hooks need some data stored in the database, instead of each hook performing the same query two or more times, just temporarily store the result in the shared scope and use it from there.

I did some tests but the context is not the same between hooks, and I'm not sure if this.context would be the place to store that data.

Direct access methods usage

Circumventing hooks don't seem do be working for me. I'm probably doing something wrong, but each time I call a method using direct access nothing get returned/executed.

If I run: Collection.direct.findOne("qPiL3bSaGknGETpGd") on the console, it returns: "function () { [native code] }" or just "function()" (depending on the browser I'm using). I was expecting a mongodb document to be returned in this case.

As with the update, nothing gets updated.

Tests fail drastically when collection hooks is added

Meteor official tests fail drastically when collection hooks package is added. This simply means that collection hooks are not suitable for production. Tests should not be failing. Using a default project created by meteor create, adding packages:

  • standard-app-packages
  • autopublish
  • insecure
  • preserve-inputs
  • collection-hooks
  • accounts-base

Run mrt test-packages and open browser. You will get a long list of tests which failed:

tinytest - accounts - expire numeric token
tinytest - passwords - basic login with password
tinytest - passwords - plain text passwords
tinytest - passwords - changing passwords
tinytest - passwords - new user hooks
tinytest - passwords - Meteor.user()
tinytest - passwords - allow rules
tinytest - passwords - tokens
tinytest - accounts emails - reset password flow
tinytest - accounts emails - verify email flow
tinytest - accounts emails - enroll account flow
tinytest - collection - update options, STRING
tinytest - collection - update options, MONGO

Without this package added tests work correctly.

Hooks are not run for insecure collections

Because of the way the mutator methods are called in https://github.com/meteor/meteor/blob/master/packages/mongo-livedata/collection.js, (i.e. by using self._collection[method].call, a client doing insert, update, or remove will not trigger the hooks on the server if the collection is insecure. This is because it's directly calling the _collection method instead of the prototype.

One way to get around this in implementation is to simply force a validator (even all true) on the collection that is being hooked. However, if it's possible to fix this, it would make things more consistent.

All this is just pointing more toward a proper integration with Meteor :)

Support Smart Collections

https://github.com/arunoda/meteor-smart-collections

Only Meteor.Collection gets mutated by this package, so I can't switch to SmartCollections yet.

There could well be a separate package like meteor-smart-collection-hooks — I haven't assessed the difficulty of this at all, and I don't know if Smart Collections have any high-level implementation differences that would invalidate the current approach used to hijack Meteor.Collection.

cc @arunoda

Collection hooks defined in server folder

Hello..

I am currently trying to use an "afterInsert" hook on one of my collections.

This collection is defined in the folder models/collections/.

The folder models/ also contains another folder named schemas, used to store "Simple-Schema" schemas. This is not revelant here, but i just want to precise, because its usual to place collections files in collections/

Because i want my collection hook to be exectuted on the server, i wrote it into a file which is stored in server/collections/hooks/. I could've add Meteor.isServer() after the definition of my collection, but even if this code would be only executed by the server, i understood that it would be sent to the client.

Doing everything like that, i got an erreor at meteor startup (full stacktrace) :
ReferenceError: VServers is not defined
where VServers if of course my collection name.

I ensure you i dont use the keyword var to define my collection.

Any idea of how i can do things ?

Meteor Collection Hooks mainpage says, "Works across both client, server or a mix", but i would like it to work only on the server, i do not trust the user for the operation that will be performed by the hook.

Regards,

William

find/findOne userId tests

I just spent some time fixing the find/findOne userId tests (5de03e1), and wanted to document some weird stuff in case someone wanted to look into it.

First, the original structure of the tests had some logical problems. The test.notEqual assertion was run inside the hook to check for the userId. This assumed that the hook was actually being run in the first place. If it wasn't, the test would pass tautologically. I guess this is okay if something else checks for the hook, but it seemed to be safer to not assume anything for this test if we didn't have to.

The tests are also created in the publish function to get around the asynchrony issues that @matb33 was having before. However, this results in some complaining from Tinytest when the publish function is hit again. I'm interested in seeing any creative ways to fix this problem so the test works across reloads (see below.)

Otherwise, I observed two weird issues:

  1. Other (non find/findOne) tests pass upon the first load of the Tinytest page, but not on subsequent ones. The find/findOne tests still pass. After poking around a bit, I think this is due to improper ignoring of the hooks that were added during these tests. Would be great if someone could look into it.
  2. Tinytest.add/Tinytest.addAsync doesn't seem to work inside InsecureLogin.ready() for some reason. (Test just don't appear.) See tests/find_findone_userid.js. I moved them out for now, and on my computer the login happens quickly enough for the tests to pass. However, it isn't logically correct.

Finally, do we need to add tests for userId being available in find/findOne calls from server side methods? This is way less complicated than the publish case, as the Meteor.userId() is typically already available. But just wondering...

P.S. Loving the new two-space soft tabs that you are using. Makes it much easier to contribute!

Question: disabling and re-enabling

Is there a good approach for disabling a collection hook after it has been set. I've tried setting the listeners to null or an empty function, e.g. Collectibles.after.insert(function(){}); and Collectibles.after.insert(null); as well as Collectibles.after.insert = null; but the handlers I had previously set still get called or when I attempt re-enable listeners TypeError: Property 'insert' of object #<Object> is not a function.

Thanks

Performance issue with this.previous

To supply this.previous document, after hooks fetch all documents affected by update and then spoon-feed them one by one to each call. However, on large updates with {multi: true} this results in high memory usage. Besides, not all hooks even use this.previous, so for those it's just a waste of resources.

Could you please implement a this.getPrevious() method + fetchPrevious option (defaults to true for backwards compatibility)? It will really help people who need performance (like me with Pintask)

server side Hooks with insert/update/remove client side

Hello!

Is there a way that hooks are only executed server side when I do a query on client side?

example (does not work):
The aim is to increment Stats each time I do queries.

Stats = new Meteor.Collection2("Stats", {
schema: {
day: {
type: Date,
label: "Date of the day",
unique: true,
index:true
},
count: {
type: Number,
label: "number of queries",
optional: true
}}})


Stats.allow({
insert: function (id, doc) {
return false
},
update: function (userId, doc, fieldNames, modifier) {
return false
},
remove: function () {
return false
}
})

Querys = new Meteor.Collection("Querys", { schema: { title: { type: String, label: "Title of query", }}}) Querys.after.insert(function (userId, doc) { Stats.upsert({day:today()},{$inc: { count: 1 }}) }); Querys.after.update(function (userId, doc, fieldNames, modifier, options) { Stats.upsert({day:today()},{$inc: { count: 1 }}) }); Querys.after.remove(function (userId, doc) { Stats.upsert({day:today()},{$inc: { count: 1 }}) }); Querys.allow({ insert: function (id, doc) { return true }, update: function (userId, doc, fieldNames, modifier) { return true }, remove: function () { return true } })

This won't work on Client since I set all Stats.allow to false so that users are not allowed to write into Stats

Question on designing hooks

I have a particular use case where a collection has different update operations on it. Each update operation should trigger a different hook. Eg. Liking a document, editing the timestamp, raising a flag, adding collaborators, etc.

At the moment, in my hooks I have to validate fields / modifiers to determine which operation is running. This becomes very hard to maintain in the long run and I am wondering whether there is a better way to design my hooks.

Or should I just fall back to writing my own meteor method instead?

TypeError: Cannot call method insert of undefined

I am getting => TypeError: Cannot call method 'insert' of undefined

IMAGES = new Meteor.Collection('images');

IMAGES.before.insert(function(userId, doc) {
    //...
});

I am using Meteor 0.8.3 and the packages Autoform, Collection2 & SimpleSchema...
Does this package conflict with the mentioned above ??

Weird behaviour on before.insert - failing silently

I have a before.insert for a given collection, the code (supposedly) runs on both client and server, but for some reason, sometimes the hook is called only in the client, but it does not run on the server.
In the client, everything works just fine, I even get an _id for the document I just inserted in my collection. But on the server, the document never gets there, and the before hook is never called (or gets called, but fails silently).
Anyone been through a similar issue? I will try to gather more info, but this is being a pain to debug, as it happens randomly.
Update: This is most definitely related to meteor-collection-hooks, as removing the hook make the insertion works just fine.
Update2: The problem was in my code. Sorry!

Local collections

I have a scenario I need som help with.

I want some of my hooks to run only server-side if it's a regular collection
and if it's a client-only collection i want the hook to run client side.

Is it possible today?

Integration with Collection2

I am trying to integrate collection hooks with Collection2 but i am getting the following error.

20140306-15:13:41.021(8)? (STDERR) Error: use "new" to construct a Meteor.Collection
W20140306-15:13:41.022(8)? (STDERR) at new Meteor.Collection (packages/mongo-livedata/collection.js:7)
W20140306-15:13:41.022(8)? (STDERR) at Meteor.Collection (packages/collection2/collection2.js:47)
W20140306-15:13:41.022(8)? (STDERR) at collections/connections.coffee:1:19

Make userId available when a hooked function is called inside a Meteor.publish callback

Hooks are great when used on the client. However, they are a bit hobbled on the server. It would be great to use them naturally inside publish functions.

This entails two issues. The first is just trying to set the userId at all:

Meteor.Collection.prototype.find = function (doc, callback) {
        var result, userId = getUserId.call(this);

Even if we do something like foo.find.call(this) inside a publish, the getUserId call does not work properly. It'll throw an error like TypeError: Object #<Object> has no method '_getFindSelector'. So there's no way to hook the userId into a server-side find right now.

Second, it would be great to somehow make this.userId directly available to the hook, without having to change the API usage. i.e, foo.find() should be callable as normal inside a publish, and the find can be filtered by userId or whatever.

A workaround for now would be to allow the userId to be passed in as an optional argument to the functions. This at least makes it possible, although doesn't fix the change to the API.

Generate document that would result from the update in "before.update"

I would like to perform operations depending on an embedded array (in this specific case, to calculate counts, averages, etc), which is getting changed through $addToSet.

Isn't there a magical function that would take (doc, modifier) as parameters and generate the final document pre-update (ie before the update is actually done)?

getUserId doesn't work

As reported by Andrew Mao offline:

the getUserId.call(this) doesn't work, because this is set to the collection inside the prototype, so you can't pass in the context of the published callback even if you wanted to

Enable automatic testing on this package with Travis

Hi @matb33,

I just fixed the tests in this package for 0.7.2 in 8124885.

Since this package has Tinytest set up, it would be sweet to set up automatic testing. It just requires a few things:

  1. Enroll your GitHub account at https://travis-ci.org and enable automatic testing for this repo

  2. Add the following to the root of the repo as .travis.yml:

    language: node_js
    node_js:
      - "0.10"
    before_install:
      - "curl -L http://git.io/ejPSng | /bin/sh"
    
  3. (optional): Add the cool little build badge with this Markdown in the README:

    [![Build Status](https://travis-ci.org/matb33/meteor-collection-hooks.png?branch=master)](https://travis-ci.org/matb33/meteor-collection-hooks)
    
  4. Push the new commit.

For more information see https://github.com/arunoda/travis-ci-meteor-packages. I have this running for https://github.com/mizzao/meteor-user-status, here: https://travis-ci.org/mizzao/meteor-user-status. The integration is really nice, you get the nice little badge that shows everything is working, and you don't have to meteor test-packages yourself!

Plus there is the added benefit that tests are run for all pull requests, so if everything passes and it doesn't look kooky, you can just auto merge. Saves a lot of work!

Exception in delivering result of invoking

Hey.

I am getting this exception while using an after.insert hook, but the callback is well executed tho. Is this a bug inside the package, or am i using it badly ?


Exception in delivering result of invoking '/vservers/insert': TypeError: Object # has no method 'apply'
    at http://localhost:3004/packages/collection-hooks.js?8023554a1294990dd1d66365a38eff93f309ce75:228:22

Being able to modify selector for remove hooks

The current API for the remove function seems to be missing a couple of use cases.

Would it be possible to modify the selector if a global remove (i.e., not by _id) was being done on the server side? Also in that case, what would be passed in for doc?

Additionally, not having access to the selector for the before.remove hook makes it impossible to implement a direct remove (for testing purposes.) It's really quite nasty how the direct find and direct insert were implemented too, so I would love to have some alternatives.

collection-hooks causing operations to fail silently

Hi Mathieu,

I filed a bug with Meteor after observing some really strange behavior in my app with collection operations failing randomly (see meteor/meteor#1289) but then realized it was a collection-hooks problem.

If you want to see what is going on, open http://crowdmapper.meteor.com in different browsers, and try to add and delete some rows on the 'Events' tab.

Should I debug the code in master or just switch to aop and help you with that?

Use a delay function iside a before update hook

Is there a way I could use a timer or a delay function inside a before.update hook to delay the actual update method?

I'm firing a dom animation inside the before.update hook which obviously has a certain duration. Returning 'true' on the callback of the animation doesn't work, as the update method is fired before the callback has a chance to run.

The solution I have right now is a really sloppy one. I call the update method I need, go through the hook, fire the animation, return false on the hook (to invalidate the update method). Then, on the callback of the animation I call the initial update method a second time. This time, sending alongside a Session variable to invalidate the conditional and return true. This way, a regular update is fired when the animation is done.

Allow calling insert/update/remove directly

Use case: I want other updates to the same collection to trigger based on the collection-hooks-overridden version of Meteor.Collection.update being called. But I don't want to go into an infinite callback loop.

For now I'm just going to use a flag variable, but would be nice to have something like Meteor.Collection.directUpdate to work with.

Hooks trigger changes

Collection.before.insert(function (userId, doc) {
  doc.active = true;
  doc.createdAt = Date.now();
});

I am not sure that this is by design, but when I insert a document and added and change event fire. The added is for the added document and the change is for the active and createdAt from the hook.

Meteor.users.find() returns zero results, Meteor.users.find({}) works.

I upgraded from 0.5.4 to 0.6 and noticed that Meteor.users.find() is now broken. This opened up a pretty serious security hole in my app, onCreateUser I check if there are zero users in the collection and if there is I give the user being created admin rights. Because this is now broken all new users get admin rights. I've locked my smart.json to 0.5.4 to address this for now.

Each documents of collection can attach some default values while insert or update

i always add createdAt, createdBy, timestamp and such keys on each 'collection.before.insert' like

Projects.before.insert (userId, project) ->
    project.createdAt = new Date()
    project.timestamp = Date.now()
    project.creatorId = Meteor.userId()
    project.createdBy = Meteor.user().username

Tasks.before.insert (userId, task) ->
    task.createdAt = new Date()
    task.timestamp = Date.now()
    task.creatorId = Meteor.userId()
    task.createdBy = Meteor.user().username

but some collections do not need attaching

Could we add some feature like we can allow some collections do attach default values ?

maybe something like

[Projects, Tasks].before.insert (userId, doc) ->
    doc.createdAt = new Date()

[Projects, Tasks].before.update (userId, doc) ->
    doc.updatedAt = new Date()

before/after save?

Hi,

thanks for the great job!

I just wanted to know if you have plans to include a before/after save hook as well?

mutating the [[Prototype]] of an object will cause your code to run very slowly

Question: On Firefox, have the following warning.

TypeError: mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create.

The debugger points to CollectionHooks.reassignPrototype. I takes two arguments, i.e. instance and constr, but when I search the code I only find one reference to the function in user-compat.js. It's called with only one parameter, i.e. CollectionHooks.reassignPrototype(Meteor.users);
I'm assuming it gets called or Firefox would not be complaining.

I guess the questions are:

  1. Does the function act as expected, considering the missing prototype?
  2. Could Firefox be outputing the message even thought it's not called, i.e. a parsing check?
  3. Is there an different approach that would not have a warning and performance penalty?

Publication for Meteor 0.9

I've prepared the package for Meteor 0.9 compatibility in 739522e. Note that automated testing doesn't currently work for this version, but you can verify the tests with 0.9.0-rc5 (and probably any newer versions since.)

You will probably want to publish it as matb33:[email protected] after updating the versions in package.js and History.md. Note that the package will need to be in a directory called matb33:collection-hooks for meteor-publish to work currently (see meteor/meteor#2388).

There are still some kinks being worked out; see https://hackpad.com/Migrating-Packages-zN0we9sIjkH and https://meteor.hackpad.com/Unipackage-tvas8pXYMOW#:h=Publishing-a-New-Package. If you'd like to add me as a maintainer I'd be happy to help publish as well.

Let me know if I can help with any issues. The big discussion thread is over at https://groups.google.com/forum/#!topic/meteor-talk/VUaWMwV7aHM.

Run specific hooks on demand

Is there currently a way to run some specific hook or set of hooks on demand?

Say I have a set of before update hooks on a collection. Most of the time I want them all to run. But on some operations that update multiple documents at once, for performance reasons, I would rather use the direct update, and then perform just one of the hooks on the changed documents.

By the way, thanks @matb33 for the fix on direct operations. It's working great now.

Add tests for userId being available in client functions and server methods

I like the new changes a lot and I think it may even call for a bump in the major version number :)

However, we are missing a few tests which were basically in the coffeescript file I had before. We should test that userId loads properly on client hooks (it didn't prior to this version) and also inside Meteor methods. You can add these to your existing test files as you see fit, but I have the same functionality inside client_server_userId_tests.coffee

Once that's done, it's probably time for a merge into master and mrt release!

Also, when running the current tests, this strange error appears in the server console as a result of calling the publish callback:

Exception from sub L46TDhKxTQHm5Bxpt TypeError: Cannot set property 'mNkv4Kqma6DZMyJxh' of undefined
    at Meteor.methods.tinytest/run.onReport (app/packages/tinytest/tinytest_server.js:50:38)
    at _.extend._report (app/packages/tinytest/tinytest.js:476:10)
    at test.run.totalTime [as onEvent] (app/packages/tinytest/tinytest.js:396:14)
    at _.extend.ok (app/packages/tinytest/tinytest.js:27:10)
    at _.extend.equal (app/packages/tinytest/tinytest.js:136:12)
    at Tinytest.addAsync.CollectionAfterFindOne.update.a (app/packages/collection-hooks/tests_userid_in_find_hooks_within_publish.js:74:10)
    at delegate (app/packages/collection-hooks/collection-hooks.js:19:12)
    at Meteor.Collection.findOne (app/packages/collection-hooks/collection-hooks.js:81:13)
    at Tinytest.addAsync.CollectionBeforeFind.update.a (app/packages/collection-hooks/tests_userid_in_find_hooks_within_publish.js:37:37)
    at Meteor.publish [as _handler] (app/packages/collection-hooks/collection-hooks.js:221:20)

currentUserId may be lost in a publish function with yielding operations

I was just reading some of our past discussions about collection hooks. In particular, in considering the use of global variables, it occurred to me that the value of currentUserId might be overwritten when yielding operations happen inside a publish function and a different publish function runs a find operation. I don't think find itself yields, but forEach probably would, and then a find used after that might have the wrong userId.

I didn't know how to solve this at the time, but the proper solution to this would be to bind the publish userId in a Meteor.EnvironmentVariable and call the original publish function using the withValue function of this variable. This would ensure that after any yielding operations, the function will still always see the same userId.

In fact, this is how Meteor.userId() itself is implemented; it's just too bad that it depends on DDP.currentInvocation and is therefore not available in publish functions: https://github.com/meteor/meteor/blob/devel/packages/accounts-base/accounts_server.js#L7

I can implement this and add a test for it when I get a moment - just writing it down for now.

Object.create

Meteor supports IE8, but IE8 doesn't support Object.create, so I guess it should not be used in packages :)

Provide <collection>.{before,after}.all()

It'd be nice to have a combo hook for before/after insert, update, or remove. My use case is that I'm synchronizing the app with Google Calendar whenever any tasks are added, updated, or removed. I don't even need to know what actually happened (the sync logic takes care of figuring all that out). It'd just save a couple lines of code, so I thought to suggest it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.