Search code examples
javascriptmeteorcronmeteor-collections

Implementing percolate:synced-cron in meteor, in order to a schedule a insert into a collection


It has been recommended to me to use this package synced-cron in order to schedule executions of code. I have the following code which at the time does nothing. My concept is that with this package when a schedule is set up a collection is created and all the data passed into the schedule is stored until at the specified time in the future you can execute the same code in a different function, in my case which would insert into a Posts collection which would then display on a template.

TLDR: User inserts data, User chooses date to post it on, synced-cron stores data in cronHistory collection along with user chosen date. When date is reached, the same data is inserted into the Posts collection.

My assumption is this is a result of my partial knowledge of programming, without understanding the fundamentals. So if anyone has a suggestion for learning core concepts I am open as well, since I think I waste a lot of time asking questions when it is really a basic concept I am missing.

Thanks! ahead of time for any help.

Here is my code with the folder structure.

**client**

/client/posts/post_item.js

Template.postItem.rendered = function(){

  var post = {
    title: "testTitleTime",
    postContent: "timeContentRest"
  };
  var time = "at 2:30 pm";
  Meteor.call('schedulePosts',post,time, function(error, result) {});

};

So my theory here is that data is passed to the meteor method schedulePosts which is on the server. My intention is to have it stored in what the documentation says is a collection called cronHistory. Although I may be completely misinterpreting what cronHistory is.

**lib**

/lib/posts.js

Meteor.methods({
  postInsert: function(postAttributes) {
    check(Meteor.userId(), String);
    check(postAttributes, {
      title: String,
      postContent: String

    });
    var user = Meteor.user();
    var post = _.extend(postAttributes, {
      userId: user._id,
      author: user.username,
      submitted: new Date()
    });
    var postId = Posts.insert(post);
    return {
      _id: postId
    };
  }
});

I know this code works if the correct attributes are passed to it so I am not to worried about it, but it needs to work in flow with everything else.

**server**

/server/schedule.js

Meteor.methods({
  schedulePosts: function (time, post) {

  SyncedCron.add({
    name: 'Crunch some important numbers for the marketing department',
    schedule: function(parser) {
      // parser is a later.parse object
      return parser.text(time);
    },
    job: function() {
      Meteor.call('postInsert', post, function(error, result) {});
      }

  });

  var MyLogger = function(opts) {
    console.log('Message', opts.message);
  };

  SyncedCron.start();

 }

});

What I think is happening here is the method is run, the schedule is initialized based on the data passed from post_item.js, once that time is reached the job function is run and the post data from post_item.js is inserted.

Update Trying new Code.

Here is a example of post when all the collection is subscribed to.

{
  "_id": "jgx5qWf4RoeG6u7KJ",
  "title": "1 20",
  "postContent": "See if it w",
  "timeToEnable": "2015-06-20T20:20:00.000Z",
  "userId": "t4Y9hqYQCG9gxDgnW",
  "author": "admin",
  "submitted": "2015-05-20T20:19:27.603Z"
}

When I try and use this below, nothing is returned when I change it from $lt to $gt it returns everything but, if I schedule a post for the future it outputs that as well. I am not sure what I might be doing wrong, maybe it's how my timeToEnable date is formatted?

Meteor.publish('posts', function() {
    var selector = {timeToEnable: {$lt: new Date()}};
    return Posts.find(selector);
});

Solution

  • I don't think syncedCron is right for your job - you want to do something once at a scheduled time in the future. Whereas cron jobs are jobs that you want to do regularly again and again at a fixed time, every 2 hours, or every Wednesday at midnight.

    You can probably forget syncedCron and get meteor to do it for you, because Meteor is reactive.

    Put the post into the Posts collection straight away, because finding a record later and writing it a second time into the posts collection is a complication.

    Add a timeToEnable field to your posts:

     {title: String,
      postContent: String
      user: Meteor.user();
      userId: user._id,
      author: user.username,
      submitted: new Date()
      timeToEnable: new Date(year, month, day, hours, minutes, seconds, milliseconds
    }
    

    Then in your server code you publish the posts collection, with a query that finds all posts with timeToEnable < now

    Meteor.publish('posts', function() {
        var selector = {timeToEnable: {$lt: new Date()}};
        return Posts.find(selector);
    });
    

    Then on the client you subscribe to the publication, and show the list of your posts in your Blaze template in the usual way.

    I believe it's that simple: this is what Meteor is all about. Your ui will just update all by itself.

    I have created a meteorpad application here:

    http://meteorpad.com/pad/EpAmSL68hZ7L3ig5j/futurePost

    You need to add reactive-dict to your application because Meteor reacts to database changes, whereas we need to react to time changes - which mean nothing to Meteor without reactive-dict.

    The meteor pad app on every page refresh (in template.onCreated()) calls a server method which creates six records, with a timeToEnable every five seconds. Just wait and you will see a record appear every five seconds.