LoginSignup
5
4

More than 5 years have passed since last update.

Meteor Guideで紹介されているコマンド・パッケージ・ソースコード一覧

Posted at

Meteor Guideで紹介されているコマンド・パッケージ・ソースコード一覧です。個人的に使わなさそうなものは除外しています。また検索性重視で1ページにまとめています。随時更新。

:book: Introduction

# Meteor インストール
curl https://install.meteor.com/ | sh

# 新規 Meteor アプリ作成
meteor create myapp

# ローカルで Meteor 起動
cd myapp
meteor npm install
meteor

:book: Code Style

meteor npm install --save-dev eslint-config-airbnb eslint-plugin-import eslint-plugin-meteor eslint-plugin-react eslint-plugin-jsx-a11y eslint
package.json
{
  ...
  "scripts": {
    "lint": "eslint .",
    "pretest": "npm run lint --silent"
  },
  "eslintConfig": {
    "plugins": [
      "meteor"
    ],
    "extends": [
      "airbnb",
      "plugin:meteor/recommended"
    ],
    "rules": {
      "meteor/eventmap-params": [
        2, { "templateInstanceParamName": "instance" }
      ],
      "import/no-unresolved": [
        2, { "ignore": ["^meteor/"] }
      ]
    }
  }
}
# ESLint 実行
meteor npm run lint
# for Atom
apm install language-babel
apm install linter
apm install linter-eslint

:book: Application Structure

Introduction to using import and export

import '../../api/lists/methods.js';  // import from relative path
import '/imports/startup/client';     // import module with index.js from absolute path
import './loading.html';              // import Blaze compiled HTML from relative path
import '/imports/ui/style.css';       // import CSS from absolute path
export const listRenderHold = LaunchScreen.hold();  // named export
export { Todos };                                   // named export
export default Lists;                               // default export
export default new Collection('lists');             // default export

Example directory layout

imports/
  startup/
    client/
      index.js                 # import client startup through a single index entry point
      routes.js                # set up all routes in the app
      useraccounts-configuration.js # configure login templates
    server/
      fixtures.js              # fill the DB with example data on startup
      index.js                 # import server startup through a single index entry point

  api/
    lists/                     # a unit of domain logic
      server/
        publications.js        # all list-related publications
        publications.tests.js  # tests for the list publications
      lists.js                 # definition of the Lists collection
      lists.tests.js           # tests for the behavior of that collection
      methods.js               # methods related to lists
      methods.tests.js         # tests for those methods

  ui/
    components/                # all reusable components in the application
                               # can be split by domain if there are many
    layouts/                   # wrapper components for behaviour and visuals
    pages/                     # entry points for rendering used by the router

client/
  main.js                      # client entry point, imports all client code

server/
  main.js                      # server entry point, imports all server code

:book: Migrating to Meteor 1.3

飛ばします。

:book: Collections and Schemas

// コレクションの作成
Todos = new Mongo.Collection('Todos');
// ローカルコレクションの作成
SelectedTodos = new Mongo.Collection(null);

SimpleSchema

meteor add aldeed:simple-schema
Lists.schema = new SimpleSchema({
  name: {type: String},
  incompleteCount: {type: Number, defaultValue: 0},
  userId: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true}
});

Collection2

meteor add aldeed:collection2
Lists.attachSchema(Lists.schema);

defaultValue and data cleaning

class ListsCollection extends Mongo.Collection {
  insert(list, callback) {
    if (!list.name) {
      let nextLetter = 'A';
      list.name = `List ${nextLetter}`;

      while (!!this.findOne({name: list.name})) {
        // not going to be too smart here, can go past Z
        nextLetter = String.fromCharCode(nextLetter.charCodeAt(0) + 1);
        list.name = `List ${nextLetter}`;
      }
    }

    // Call the original `insert` method, which will validate
    // against the schema
    return super.insert(list, callback);
  }
}

Lists = new ListsCollection('Lists');

Hooks on insert/update/remove

class ListsCollection extends Mongo.Collection {
  // ...
  remove(selector, callback) {
    Package.todos.Todos.remove({listId: selector});
    return super.remove(selector, callback);
  }
}

Migrating to a new schema

meteor add percolate:migrations
Migrations.add({
  version: 1,
  up() {
    Lists.find({todoCount: {$exists: false}}).forEach(list => {
      const todoCount = Todos.find({listId: list._id})).count();
      Lists.update(list._id, {$set: {todoCount}});
    });
  },
  down() {
    Lists.update({}, {$unset: {todoCount: true}});
  }
});

Bulk changes

Migrations.add({
  version: 1,
  up() {
    // This is how to get access to the raw MongoDB node collection that the Meteor server collection wraps
    const batch = Lists.rawCollection().initializeUnorderedBulkOp();
    Lists.find({todoCount: {$exists: false}}).forEach(list => {
      const todoCount = Todos.find({listId: list._id}).count();
      // We have to use pure MongoDB syntax here, thus the `{_id: X}`
      batch.find({_id: list._id}).updateOne({$set: {todoCount}});
    });

    // We need to wrap the async function to get a synchronous API that migrations expects
    const execute = Meteor.wrapAsync(batch.execute, batch);
    return execute();
  },
  down() {
    Lists.update({}, {$unset: {todoCount: true}});
  }
});

Running migrations

// After running `meteor shell` on the command line:
Migrations.migrateTo('latest');

Breaking schema changes

javascript
// The "0" migration is the unmigrated (before the first migration) state
Migrations.migrateTo(0);

Collection helpers

meteor add dburles:collection-helpers
Lists.helpers({
  // A list is considered to be private if it has a userId set
  isPrivate() {
    return !!this.userId;
  }
});
const list = Lists.findOne();
if (list.isPrivate()) {
  console.log('The first list is private!');
}

Association helpers

Lists.helpers({
  todos() {
    return Todos.find({listId: this._id}, {sort: {createdAt: -1}});
  }
});
const list = Lists.findOne();
console.log(`The first list has ${list.todos().count()} todos`);

:book: Publications and Data Loading

Defining a publication

Meteor.publish('lists.public', function() {
  return Lists.find({
    userId: {$exists: false}
  }, {
    fields: Lists.publicFields
  });
});
Meteor.publish('lists.private', function() {
  if (!this.userId) {
    return this.ready();
  }

  return Lists.find({
    userId: this.userId
  }, {
    fields: Lists.publicFields
  });
});
Meteor.publish('todos.inList', function(listId) {
  // We need to check the `listId` is the type we expect
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  // ...
});

Subscribing to data

const handle = Meteor.subscribe('lists.public');
const handle2 = Meteor.subscribe('todos.inList', list._id);

Subscribe in UI components

Template.Lists_show_page.onCreated(function() {
  this.getListId = () => FlowRouter.getParam('_id');

  this.autorun(() => {
    this.subscribe('todos.inList', this.getListId());
  });
});

Subscription readiness

const handle = Meteor.subscribe('lists.public');
Tracker.autorun(() => {
  const isReady = handle.ready();
  console.log(`Handle is ${isReady ? 'ready' : 'not ready'}`);  
});

FindFromPublication

meteor add percolate:find-from-publication

PublishCounts

meteor add tmeasday:publish-counts 
Meteor.publish('Lists.todoCount', function({ listId }) {
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  Counts.publish(this, `Lists.todoCount.${listId}`, Todos.find({listId}));
});
Counts.get(`Lists.todoCount.${listId}`)

ReactiveVar / ReactiveDict

meteor add reactive-var
meteor add reactive-dict

PublishComposite

meteor add reywood:publish-composite

Loading data from a REST endpoint with a publication

const POLL_INTERVAL = 5000;

Meteor.publish('polled-publication', function() {
  const publishedKeys = {};

  const poll = () => {
    // Let's assume the data comes back as an array of JSON documents, with an _id field, for simplicity
    const data = HTTP.get(REST_URL, REST_OPTIONS);

    data.forEach((doc) => {
      if (publishedKeys[doc._id]) {
        this.changed(COLLECTION_NAME, doc._id, doc);
      } else {
        publishedKeys[doc._id] = true;
        if (publishedKeys[doc._id]) {
          this.added(COLLECTION_NAME, doc._id, doc);
        }
      }
    });
  };

  poll();
  this.ready();

  const interval = Meteor.setInterval(poll, POLL_INTERVAL);

  this.onStop(() => {
    Meteor.clearInterval(interval);
  });
});

simple:rest パッケージ

meteor add simple:rest

:book: Methods

Defining

Meteor.methods({
  'todos.updateText'({ todoId, newText }) {
    new SimpleSchema({
      todoId: { type: String },
      newText: { type: String }
    }).validate({ todoId, newText });

    const todo = Todos.findOne(todoId);

    if (!todo.editableBy(this.userId)) {
      throw new Meteor.Error('todos.updateText.unauthorized',
        'Cannot edit todos in a private list that is not yours');
    }

    Todos.update(todoId, {
      $set: { text: newText }
    });
  }
});

Calling

Meteor.call('todos.updateText', {
  todoId: '12345',
  newText: 'This is a todo item.'
}, (err, res) => {
  if (err) {
    alert(err);
  } else {
    // success!
  }
});

Advanced Methods with mdg:validated-method

meteor add mdg:validated-method
export const updateText = new ValidatedMethod({
  name: 'todos.updateText',
  validate: new SimpleSchema({
    todoId: { type: String },
    newText: { type: String }
  }).validator(),
  run({ todoId, newText }) {
    const todo = Todos.findOne(todoId);

    if (!todo.editableBy(this.userId)) {
      throw new Meteor.Error('todos.updateText.unauthorized',
        'Cannot edit todos in a private list that is not yours');
    }

    Todos.update(todoId, {
      $set: { text: newText }
    });
  }
});

Handling errors

// Call the Method
updateText.call({
  todoId: '12345',
  newText: 'This is a todo item.'
}, (err, res) => {
  if (err) {
    if (err.error === 'todos.updateText.unauthorized') {
      // Displaying an alert is probably not what you would do in
      // a real app; you should have some nice UI to display this
      // error, and probably use an i18n library to generate the
      // message from the error code.
      alert('You aren\'t allowed to edit this todo item');
    } else {
      // Unexpected error, handle it in the UI somehow
    }
  } else {
    // success!
  }
});

:book: Users and Accounts

meteor add accounts-ui
{{> loginButtons}}
# pick one or more of the below
meteor add accounts-password
meteor add accounts-facebook
meteor add accounts-google
meteor add accounts-github
meteor add accounts-twitter
meteor add accounts-meetup
meteor add accounts-meteor-developer
meteor add useraccounts:core useraccounts:unstyled
meteor add useraccounts:flow-routing

Publishing custom data

Meteor.publish('Meteor.users.initials', function ({ userIds }) {
  // Validate the arguments to be what we expect
  new SimpleSchema({
    userIds: { type: [String] }
  }).validate({ userIds });

  // Select only the users that match the array of IDs passed in
  const selector = {
    _id: { $in: userIds }
  };

  // Only return one field, `initials`
  const options = {
    fields: { initials: 1 }
  };

  return Meteor.users.find(selector, options);
});

Roles and permissions

alanning:roles パッケージ

meteor add alanning:roles
// Give Alice the 'admin' role
Roles.addUsersToRoles(aliceUserId, 'admin', Roles.GLOBAL_GROUP);

// Give Bob the 'moderator' role for a particular category
Roles.addUsersToRoles(bobsUserId, 'moderator', categoryId);
const forumPost = Posts.findOne(postId);

const canDelete = Roles.userIsInRole(userId,
  ['admin', 'moderator'], forumPost.categoryId);

if (! canDelete) {
  throw new Meteor.Error('unauthorized',
    'Only admins and moderators can delete posts.');
}

Posts.remove(postId);

Per-document permissions

Lists.helpers({
  // ...
  editableBy(userId) {
    if (!this.userId) {
      return true;
    }

    return this.userId === userId;
  },
  // ...
});
const list = Lists.findOne(listId);

if (! list.editableBy(userId)) {
  throw new Meteor.Error('unauthorized',
    'Only list owners can edit private lists.');
}

:book: Testing | Meteor Guide

meteor add practicalmeteor:mocha

meteor add xolvio:cleaner

meteor add dburles:factory

meteor add hwillson:stub-collections

meteor add velocity:meteor-stubs

# for React
meteor npm install -D enzyme

# Testing publications
meteor add johanbrook:publication-collector
# Running unit tests
meteor test --driver-package practicalmeteor:mocha --port 3100

# Running full-app tests
meteor test --full-app --driver-package practicalmeteor:mocha

Acceptance testing

# node >= 4
npm install --global chimp
package.json
{
  "scripts": {
    "chimp-watch": "chimp --ddp=http://localhost:3000 --watch --mocha --path=tests",
    "chimp-test": "chimp --mocha --path=tests"
  }
}

Running acceptance tests

# In one terminal
meteor

# In another
meteor npm run chimp-watch

Creating data

meteor add tmeasday:acceptance-test-driver
meteor test --full-app --driver-package tmeasday:acceptance-test-driver

Continuous Integration

Command line

meteor add dispatch:mocha-phantomjs
meteor test --once --driver-package dispatch:mocha-phantomjs
{
  "scripts": {
    "test": "meteor test --once --driver-package dispatch:mocha-phantomjs"
  }
}

CircleCI

circle.yml
machine:
  node:
    version: 0.10.43
dependencies:
  override:
    - curl https://install.meteor.com | /bin/sh
    - npm install
checkout:
  post:
    - git submodule update --init

:book: URLs and Routing

meteor add kadira:flow-router
meteor add zimme:active-route
meteor add arillo:flow-router-helpers

# for Blaze
meteor add kadira:blaze-layout

:book: User Interfaces

# Internationalization
meteor add tap:i18n 

# Animating changes in visiblity
meteor add percolate:momentum

:book: Blaze

Use a reactive dict for state

meteor add reactive-dict
Template.Lists_show.onCreated(function() {
  this.state = new ReactiveDict();
  this.state.setDefault({
    editing: false,
    editingTodo: false
  });
});

Attach functions to the instance

import {
  updateName,
} from '../../api/lists/methods.js';

Template.Lists_show.onCreated(function() {
  this.saveList = () => {
    this.state.set('editing', false);

    updateName.call({
      listId: this.data.list._id,
      newName: this.$('[name=name]').val()
    }, (err) => {
      err && alert(err.error);
    });
  };
});
Template.Lists_show.events({
  'submit .js-edit-form'(event, instance) {
    event.preventDefault();
    instance.saveList();
  }
});

Scope DOM lookups to the template instance

Template.Lists_show.events({
  'click .js-todo-add'(event, instance) {
    instance.$('.js-todo-new input').focus();
  }
});

Passing HTML content as a template argument

meteor add kadira:blaze-layout
{{> Template.dynamic templateName dataContext}}

Controlling re-rendering

meteor add peerlibrary:computed-field

:book: React

# npm で React をインストール
meteor npm install --save react react-dom

# Using 3rd party packages
meteor npm install --save griddle-react

# Blaze を使わない場合
meteor remove blaze-html-templates
meteor add static-html

# React 内で Blaze テンプレートを使用する場合
meteor add gadicc:blaze-react-component

# ReactMeteorData
meteor add react-meteor-data
meteor npm install --save react-addons-pure-render-mixin

# Routing
meteor add kadira:flow-router
meteor npm install --save react-mounter
## or
meteor npm install --save react-router

# Using React in Atmosphere Packages
meteor add tmeasday:check-npm-versions

:book: Angular

飛ばします。

:book: Atmosphere vs. npm

飛ばします。

:book: Using Atmosphere Packages

# Searching for packages
meteor search <WORD>
meteor show kadira:flow-router

# Installing Atmosphere Packages
meteor add <PACKAGE>

# See all the Atmosphere packages
meteor list

# Remove an unwanted Atmosphere package
meteor remove <PACKAGE>

:book: Writing Atmosphere Packages

# Create a package
meteor create --package my-package

# Testing packages
meteor test-packages ./ --driver-package practicalmeteor:mocha

# Publish a package
meteor publish

:book: Using npm Packages

meteor npm install

meteor npm install --save meteor-node-stubs
meteor npm install --save moment

meteor npm shrinkwrap

Shrinkpack

npm install -g shrinkpack

meteor npm install moment
meteor npm shrinkwrap
shrinkpack

:book: Writing npm Packages

mkdir my-package
cd my-package/
meteor npm init

Including in your app

# Inside node_modules
cd my-app/node_modules/
mkdir my-package
cd my-package/
meteor npm init
git add -f ./ # or use a git submodule
# npm link
cd ~/
mkdir my-package
cd my-package/
meteor npm init
cd ~/my-app/
meteor npm link ~/my-package

:book: Mobile

meteor add-platform ios
sudo xcodebuild -license accept

meteor add-platform android
meteor remove-platform ios android
meteor list-platforms

meteor run ios
meteor run ios-device
meteor run android
meteor run android-device
# Installing plugins
meteor add cordova:cordova-plugin-camera@1.2.0

# Installing a plugin from Git
meteor add cordova:com.phonegap.plugins.facebookconnect@https://github.com/Wizcorp/phonegap-facebook-plugin.git#5dbb1583168558b4447a13235283803151cb04ec

# Installing a plugin from the local file system
meteor add cordova:cordova-plugin-underdevelopment@file://../plugins/cordova-plugin-underdevelopment

# Removing directly installed plugins
meteor remove cordova:cordova-plugin-camera
meteor remove cordova:com.phonegap.plugins.facebookconnect
meteor remove cordova:cordova-plugin-underdevelopment
# Domain whitelisting

:book: Build System

meteor add ecmascript

# CoffeeScript
meteor add coffeescript

# Sass/SCSS
meteor add fourseven:scss

# LESS
meteor add less

# Stylus
metoer add stylus

PostCSS and AutoPrefixer

meteor remove standard-minifier-css
meteor add juliancwirko:postcss
package.json
{
  "devDependencies": {
    "autoprefixer": "^6.3.1"
  },
  "postcss": {
    "plugins": {
      "autoprefixer": {"browsers": ["last 2 versions"]}
    }
  }
}

:book: Security

meteor add check
meteor add aldeed:simple-schema
meteor remove autopublish insecure

Securing API keys

{
  "facebook": {
    "clientId": "12345",
    "secret": "1234567"
  }
}
# Pass development settings when running your app locally
meteor --settings development.json

# Pass production settings when deploying your app to Galaxy
meteor deploy myapp.com --settings production.json

API keys for OAuth

meteor add service-configuration
ServiceConfiguration.configurations.upsert({
  service: "facebook"
}, {
  $set: {
    clientId: Meteor.settings.facebook.clientId,
    loginStyle: "popup",
    secret: Meteor.settings.facebook.secret
  }
});

SSL

meteor add force-ssl

:book: Deployment and Monitoring

Custom deployment

# for example if deploying to a Ubuntu linux server:
npm install --production
meteor build /path/to/build --architecture os.linux.x86_64
cd my_directory
(cd programs/server && npm install)
MONGO_URL=mongodb://localhost:27017/myapp ROOT_URL=http://my-app.com node main.js

Monitoring users via analytics

meteor add okgrow:analytics
settings.json
{
  "public": {
    "analyticsSettings": {
      // Add your analytics tracking id's here
      "Google Analytics" : {"trackingId": "Your tracking ID"}
    }
  }
}

Enabling SEO

# for Prerender.io
meteor add dfischer:prerenderio

# for Galaxy
meteor add mdg:seo

# To set <title> tags and other <head> content
meteor add kadira:dochead

ひとまずここまで。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4