Getting RESTless

with

Meteor



bit.ly/1s7TRzm

presented by

ryan jarvinen / @ryanj

Used to run the developer program at [Eventbrite](http://developer.eventbrite.com)

Realtime, multi-user experiences

## nodejs ![oaklandjs](http://d21ii91i3y6o6h.cloudfront.net/gallery_images/from_proof/1504/large/1403129381/oakland-dot-js.png)
## code for america code for Oakland
## [sudoroom](http://sudoroom.org) ![sudoroom](https://sudoroom.org/wp-content/uploads/2014/04/sudoroom_logo_small.png)

cryptoparty


  1. <3 EFF, digital rights
  2. fully homomorphic encryption
  3. tahoe-LAFS

Developer Advocates


one of 87 meetup groups

Agenda

  • Intro to Meteor
  • Build a basic Meteor app
  • Test Client-Side Security for Meteor
### Requirements: Install [Meteorjs](https://www.meteor.com/) and [MongoDB](http://www.mongodb.com/) curl https://install.meteor.com/ | sh
Code du Jour [https://github.com/ryanj/meteor-showcase](https://github.com/ryanj/meteor-showcase)

Meteor

is…

Data on the Wire

Don't send HTML over the network.
Just send data, let the client decide how to render it.

One Language

Javascript

Database Everywhere

Use the same APIs on the client and the server.

Latency Compensation

On the client, use prefetching and model simulation to make it look like you have a zero-latency connection to the database.

Full Stack Reactivity

Make realtime your default.
All layers from DB to templates should make an event-driven interface available.

Embrace the Ecosystem

Meteor is Open Source and integrates with existing open source tools and frameworks (rather than replacing them).

Simplicity Equals Productivity

The best way to make something seem simple is to have it actually be simple.

How?

fibers

Fibers, the Event Loop and Meteor

source: node-fibers

Smart Packages

meteor add bootstrap
meteor add handlebars
meteor add jquery
meteor add underscore
meteor add service-configuration
meteor add accounts-github
meteor remove insecure

Pre-9.0

meteorite

npm install --global meteorite

no longer needed!

New: Meteor package system

Meteor 9.x includes a new Meteor package system (featuring Isobuild, Meteor Package Server)

  • publish your own packages
  • over 1800 community packages
meteor add <packagename>
meteor publish
meteor search <query>

Let's look at some code!

https://github.com/ryanj/meteor-showcase

Client code and Server code

if (Meteor.isServer) {
  ...
}
if (Meteor.isClient) {
  ...
}
Showcase = new Meteor.Collection("showcase");

if (Meteor.isServer) {
  Meteor.publish("showcase-items", function () {
    return Showcase.find(); // share everything in the showcase
  });
}

if (Meteor.isClient) {
  Meteor.subscribe("showcase-items");
}
# templates

Templates (HTML)

<head>
  <title>meteorshowcase</title>
</head>

<body>
  {{> nav}}
  {{> list}}
  {{> submit}}
  {{> edit}}
  {{> view}}
</body>

Templates (HTML)

 <template name="list">
  <div class="showcase">
    {{#each items}}
      {{> item}}
      <hr/>
    {{/each}}
  </div>
</template>

Templates (HTML)

<template name="view">
<div id='modal_view' class="modal fade in"
  <div class="modal-header">
    <a href="#" data-dismiss="modal" class="close pull-right;">×</a>
    <h3 class="item_title">{{app_name}}</h3>
  </div>
  <div class="modal-body">
    <div class'image_url'><a href='{{demo_url}}'><img src='{{image_url}}'/></a></div>
    <div class='name'>{{app_name}}</div>
    <div class='description'>{{description}}</div>
    <div class='authored_by'>By: <span class='author'>{{author}}</span></div>
    <div class='date'>Submitted on: {{date}}</div>
    <div class='score'>Score: {{score}}</div>
    <div class='source_url'>Source code: <a href='{{source_url}}'>{{source_url}}</a></div>
    <p><a href="{{clone_url}}" class="shift btn btn-large btn-inverse btn-block">Run on OpenShift <b class='icon-cog'></b></a></p>
  </div>
  <div class="modal-footer">
    {{# if currentUser }}<a href="#" $('#modal_edit').modal('show') && $('#modal_view').modal('hide');" class="edit pull-left btn btn-info">Settings <b class='icon-wrench'></b></a>{{/if}}
    <a href="{{demo_url}}" onclick="console.log('Opening app...');" class="pull-right btn btn-primary">Visit <b class='icon-globe'></b></a>
    <a href="#" data-dismiss="modal" onclick="$('#modal_view').modal('hide');" class="btn pull-right">Cancel <b class='icon-remove'></b></a>
  </div>
</div>
</template>

Templates (js)

Template.list.items = function () {
  var nav_config = Session.get('nav_settings');
  var sort_by = Session.get('sort_by');
  var sort_order = {};
  var filter = Session.get('filter');
  if (!filter || filter == undefined){ filter = '' };
  sort_order[sort_by] = nav_config[sort_by] || -1;
  return Showcase.find({ name: { $regex: filter, $options: 'i'}}, {sort: sort_order}).fetch();
};

Template.nav.username = function () {
  var u = Meteor.user();
  return u && u.profile && u.profile.name;
};

Events

Template.item.events({
  'click a.inc': function () {
    Showcase.update(Session.get("selected_item"), {$inc: {score: 5}});
  },
  'click a.dec': function () {
    Showcase.update(Session.get("selected_item"), {$inc: {score: -5}});
  },
  'click a.view': function () {
    $('#modal_view').modal('show');
  }
});

Protecting DB Access

Showcase.allow({
  remove: function (userId, item) {
    var username = false;
    if(userId){
      username = Meteor.users.findOne(userId).profile.name;
    }
    return (item.author == username || username == 'ryan jarvinen');
  },
  insert: function (userId, item) {
    return (userId) ? true : false;
  },
  update: function (userId, item, fieldNames, modifier) {
update: function (userId, item, fieldNames, modifier) {
  var username = false;
  if(userId){
    username = Meteor.users.findOne(userId).profile.name;
  }
  if(item.score > 9000){
    // OVER 9000?!?!
    return false;
  }else if( _.isEqual(modifier, {$inc:{score: +5}})){
    //allow voting by anyone
    console.log('option1 - voter');
    return true;
  }else if( _.isEqual(modifier, {$inc:{score: -5}}) && item.score > 4 ){
    //allow voting by anyone
    console.log('option1 - voter');
    return true;
  }else if(username == 'ryan jarvinen') {
    //allow updates by admins (me) :-P
    console.log('option2 - admin');
    return true;
  }else{
    //allow updates by owners
    console.log('option3 - owner');
    return ( username == item.author ) ? true : false;
  }
}

3rd party auth

var github_client_id = process.env.GITHUB_CLIENT || 'cc02855bd41dbfc4be72';
var github_client_secret = process.env.GITHUB_SECRET || '443ea5d64be6afada4acd946cf5b49878f4af4c8';
ServiceConfiguration.configurations.remove({
  service: "github"
});
ServiceConfiguration.configurations.insert({
  service: "github",
  clientId: github_client_id,
  secret: github_client_secret
});

Shipping Meteor

to meteor.com:

meteor deploy

Production flag

meteor --production

Bundles

meteor bundle bundle.tar.gz

Docker Images

https://registry.hub.docker.com/search?q=meteor

Thanks for following along!   --ryanj

link to slides:

bit.ly/1s7TRzm