Skip to main content

Astronomy for Meteor

(번역중...)

이 아이를 사용하려면 지금까지 우리가 해왔던 Meteor의 개발 방식에서 어느정도는 벗어나야할 필요가 있습니다. simple schema, validated method, composit publish등 아무 생각없이 편리하게만 사용했던 라이브러리를 버려야 하기 때문입니다. 한 번에 모든 내용을 다 번역할수는 없지만 메인 문서를 번역할 예정입니다.

주의할 점

  • Class안에 method, helper를 담아서 처리하다 보니 함수들의 실행 위치가 서버인지 클라이언트인지 혼돈하는 경우가 있다. 주의가 필요하다.

Features#

Astronomy is highly modularized. Some basic features comes with the base jagi:astronomy package. Others have to be added as separate modules / packages. Here is a list of all the features with a short description.

Astronomy documents#

Documents fetched from collections are not simple JavaScript objects but instances of classes you define using Astronomy.

Fields’ types#

When defining a field list, you can specify their types like String, Number, Boolean, Date etc. Thanks to that it will automatically validate fields’ values.

Fields’ default values#

On document initialization, you may want to set default values of some fields. In Astronomy it’s easy to do. You can also use functions to compute a default value.

Nested fields / classes#

You can nest one or many class instances in a field.

Transient fields#

You can define list of fields that won’t be stored in the database. You can use transient fields as normal fields.

Immutable fields#

There are situations when you may want to mark a field as immutable so it won’t be possible to change field’s value once it has been saved into the database.

Document’s EJSON-ification#

EJSON is an extension of JSON that supports more types. When sending documents from the client to the server over the DDP protocol (for instance in Meteor methods), they get stringified using the EJSON.strinigify() function. Astronomy classes are EJSON compatible, so you can send them over the DDP.

Methods#

You can define helpers, so your document is not only a data storage but a “live” thing. A dog can bark dog.bark(); and a user can greet you user.sayHello();.

Events#

Astronomy implements an events system that allows you to hook into many processes happening inside the package. For example, you can hook into the process of saving document. You may want to lowercase user’s nickname just before saving document. With Astronomy it’s a piece of cake. The events system also provides you a way to stop an event’s propagation and to prevent default behavior.

Getters and setters#

Each Astronomy document has get() and set() methods. Thanks to them, you can get and set multiple fields at once and access nested fields in an easy way. For example you can call user.set('profile.email', 'example@mail.com') to set email address in the profile object.

Modified fields#

Thanks to the doc.getModified() method you can access fields that had been modified from the last save.

Cloning document#

It allows making copies of documents. You can automatically save cloned document or modify it before saving.

var copy = post.copy(); // Creates a new copy with an empty _id copy.title = 'Foobar'; // Make changes before saving copy.save(); // Save document

var anotherCopy = post.copy(true); // automatically saved Reloading document

Sometimes after introducing some changes into your document, you may want to reload document to its new state from the database. For that task you can use the reload() method.

Indexes#

This feature allows you to define indexes that will be created on given fields / columns to speed up the process of fetching data.

Inheritance#

When there are classes with similar schemas, sometimes it’s simpler to create a base class and extend it by adding only the features that differ.

Multiple classes in the same collection#

You can store instances of many classes in the same collection. You just have to tell Astronomy which field will store the name of the class to use when creating the instance.

Validation#

When saving a document, Astronomy will validate its values agains rules you defined. If yo haven’t defined any rules it at least validate types of fields. You can also validate a document without saving it.

Validation order#

You can define the order in which validation will take place.

Mongo queries#

You can perform insert, update, upsert and remove mongo queries directly on a class with all the benefits of Astronomy, like default fields’ values or validation User.update(id, {$set: {firstName: 'John'}});.

Behaviors#

Behavior is a nice way of reusing your code in more than one class. If you have similar features in two or more classes, you should consider creating a behavior for that feature. An example of a behavior is the Timestamp behavior which automatically sets createdAt and updateAt fields with the current date on every document save or update.

Timestamp behavior#

The Timestamp Behavior adds two fields that store information about a document’s creation and update dates. Those fields will be automatically filled with the correct date. To use this behavior you have to add it to your project meteor add jagi:astronomy-timestamp-behavior.

Slug behavior#

The Slug Behavior adds a slug field for storing a URL friendly value of a chosen field. The text Tytuł artykułu will be converted to tytul-artykulu. The slug field can then be used in routing http://localhost:3000/post/tytul-artykulu. To use this behavior you have to add it to your project meteor add jagi:astronomy-slug-behavior.

Softremove behavior#

The Softremove Behavior adds the softRemove() method to your class which prevents a document from being removed. Instead, it will flag the document as removed and you will be able to omit such documents on documents fetch. To use this behavior you have to add it to your project meteor add jagi:astronomy-softremove-behavior.

Fields definition#

Simple fields list#

import { Class } from 'meteor/jagi:astronomy';
const User = Class.create({
name: 'User',
/* ... */
fields: {
firstName: String,
createdAt: Date,
age: Number,
},
});

In the example above, we passed an object with the fields’ names as keys and the fields’ types as values. There are several predefined types. You can choose from:

위 예제에서 필드 이름이 키이고 필드 유형이 값인 객체를 전달했습니다. 사전 정의 된 유형이 몇 가지 있습니다. 다음 중에서 선택할 수 있습니다.

  • String
  • Number
  • Boolean
  • Date
  • Object
  • Mongo.ObjectID

You can also create your own types.

자신 만의 유형을 만들 수도 있습니다.

Characters allowed in field name

You can’t use the . sign in the field name. Actually, it’s a good practice to only use letters, digits and _, - characters.

필드이름에 .을 사용할 수 없습니다. 보통 문자, 숫자 및 _, - 문자만 사용하는 것이 좋습니다.

Nested fields#

MongoDB is a document-oriented database. This allows you to not only store plain values in fields also objects and arrays and Astronomy have a nice syntax for allowing that.

MongoDB는 문서 지향 데이터베이스입니다. 이것은 필드에 일반적인 값을 저장할 뿐만아니라 객체와 배열도 저장 할 수 있습니다. Astonomy는 이를 허용하는 멋진 구문이 있습니다.

import { Class } from 'meteor/jagi:astronomy';
const User = Class.create({
name: 'User',
/* ... */
fields: {
address: {
type: Object,
default: function () {
return {};
},
},
phones: {
type: [String],
default: function () {
return [];
},
},
},
});

In this example, we’ve defined the address field that can store any JavaScript object and the phones field that can store arrays of strings. In the square brackets you can use any available type like: Number, Date, Object, Boolean.

이 예제에서는 JavaScript 객체를 저장할 수있는 address 필드와 문자열 배열을 저장할 수있는 phones 필드를 정의했습니다. 대괄호 안에는 Number, Date, Object, Boolean과 같은 사용 가능한 유형을 사용할 수 있습니다.

import { Class } from 'meteor/jagi:astronomy';
const Address = Class.create({
name: 'Address',
/* No collection attribute */
fields: {
city: {
type: String,
},
state: {
type: String,
},
},
});
const User = Class.create({
name: 'User',
collection: Users,
fields: {
address: {
type: Address,
},
},
});

As you can see, we can use class constructor as a type. It’s true for all the classes defined with Astronomy. They are automatically registered as a new type. Now let’s try defining the User class, that has the addresses field for storing an array of addresses.

보시다시피 클래스 생성자를 Type으로 사용할 수 있습니다. Astonomy에서 정의 된 모든 클래스에 대해 그렇습니다. 자동으로 새 유형으로 등록됩니다. 이제 주소 배열을 저장하기위한 address 필드가있는 User 클래스를 정의해 보겠습니다.

import { Class } from 'meteor/jagi:astronomy';
const User = Class.create({
name: 'User',
collection: Users,
fields: {
addresses: {
type: [Address],
},
},
});

Helpers#

By adding helpers to your class you can make a document to be alive. A user can user.greet() you and a dog can dog.bark(). Let's take a look at the example of adding the fullName() helper to the User class.

클래스에 helpers를 추가하면 문서를 생생하게 만들 수 있습니다. user 는 user.greet() 할 수 있고 dog는 dog.bark() 할 수 있습니다. User 클래스에 fullName() 도우미를 추가하는 예제를 살펴 보겠습니다.

import { Class } from 'meteor/jagi:astronomy';
const User = Class.create({
name: 'User',
/* ... */
fields: {
firstName: String,
lastName: String,
},
helpers: {
fullName(param) {
var fullName = this.firstName + ' ' + this.lastName;
if (param === 'lower') {
return fullName.toLowerCase();
} else if (param === 'upper') {
return fullName.toUpperCase();
}
return fullName;
},
},
});
var user = new User();
user.firstName = 'John';
user.lastName = 'Smith';
user.fullName(); // Returns "John Smith"

A context this in a helper is a document instance, so you can access other fields of a document. The fullName() helper takes the firstName and lastName properties and join them with a space character and returns such a string. As you can see, we can also pass parameters to helpers.

도우미에 있는 this 컨텍스트는 문서 인스턴스이므로 문서의 다른 필드에 액세스 할 수 있습니다. fullName() helper는 firstNamelastName 속성을 사용하고 공백 문자를 사용하여 이러한 문자열을 반환합니다. 보시다시피 매개 변수를 helpers에 전달할 수도 있습니다.

Using helpers in Blaze templates

You can use Astronomy helpers in Blaze templates as normal helpers or properties. Let's take a look at the example of printing a full name of a user.

Blaze 템플릿에서 Astronomy 도우미를 일반적인 helpers 또는 properties로 사용할 수 있습니다. 사용자의 전체 이름을 출력하는 예를 살펴 보겠습니다.

{% raw %}
<div>Full name: {{user.fullName}}</div>
{% endraw %}

You can also pass parameters to helpers:

helpers에 매개 변수를 전달할 수도 있습니다.

{% raw %}
<div>Full name: {{user.fullName "upper"}}</div>
{% endraw %}

Meteor methods (>= 2.2.0)#

You can define Meteor methods directly in your class definition. Thanks to that, you can better organize your code. Executing method from the client will call it in both client and server. In previous Astronomy version you had to define helpers/methods and later Meteor methods that wrap these helpers. From version 2.2.0 you can minimize amount of code by putting all the logic in Meteor methods defined on the class level. Let's take a look how to define them.

Meteor 메소드를 클래스 정의에서 직접 정의 할 수 있습니다. 덕분에 코드를 더 잘 구성 할 수 있습니다. 클라이언트에서 메소드를 실행하면 클라이언트와 서버 모두에서 호출됩니다. 이전의 Astonomy 버전에서는 helpers/methods를 정의하고 나중에 이러한 헬퍼를 래핑하는 Meteor 메서드를 정의해야했습니다. 버전 2.2.0부터는 클래스 레벨에서 정의 된 Meteor 메소드에 모든 로직을 넣어 코드의 양을 최소화 할 수 있습니다. 정의 방법을 살펴 보겠습니다.

// user.js
import { Class } from 'meteor/jagi:astronomy';
const User = Class.create({
name: 'User',
/* ... */
fields: {
firstName: String,
lastName: String,
},
meteorMethods: {
rename(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
return this.save();
},
},
});

Now, we will execute Meteor method in the same way we execute Astronomy helpers.

이제 우리는 Astonomy 도우미를 실행하는 것과 같은 방식으로 Meteor 메서드를 실행합니다.

// client.js
const user = User.findOne();
user.rename('John', 'Smith', (err, result) => {});

As you can see, we pass three arguments to the rename() method. The first two arguments are passed to the method. The last one is a callback function which will be execute after server response with the Meteor method result as the second argument of the callback function.

보시다시피 rename() 메서드에 세 개의 인수를 전달합니다. 처음 두 인수는 메서드에 전달됩니다. 마지막 것은 Meteor 메소드 결과를 콜백 함수의 두 번째 인수로 사용하여 서버 응답 후에 실행될 콜백 함수입니다.

Server only methods

You might want to hide some method logic from the client and define Meteor method only on the server.

클라이언트에서 일부 메소드 로직을 숨기고 서버에서만 Meteor 메소드를 정의 할 수 있습니다.

// user.js
import { Class } from 'meteor/jagi:astronomy';
const User = Class.create({
name: 'User',
/* ... */
fields: {
firstName: String,
lastName: String,
},
});
// server.js
User.extend({
meteorMethods: {
rename(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
return this.save();
},
},
});

In this case calling the rename method on the client will throw error.

이 경우 클라이언트에서 rename 메소드를 호출하면 오류가 발생합니다.

// client.js
const user = User.findOne();
// Throws "Uncaught TypeError: user.rename is not a function()".
user.rename('John', 'Smith', (err, result) => {});

So how to call this method from the client? There are two special methods designed to help with that. They are callMethod() and applyMethod(). They work in similar way as Meteor.call() and Meteor.apply()

그렇다면이 메소드를 클라이언트에서 호출하는 방법은 무엇입니까? 이를 돕기 위해 설계된 두 가지 특별한 방법이 있습니다. 그것들은 callMethod()applyMethod()입니다. Meteor.call()Meteor.apply()와 비슷한 방식으로 작동합니다.

// client.js
const user = User.findOne();
user.callMethod('rename', 'John', 'Smith', (err, result) => {});
// or
user.applyMethod('rename', ['John', 'Smith'], (err, result) => {});

The difference between these two method is that the second one takes an array of arguments.

이 두 메서드의 차이점은 두 번째 메서드가 배열인수을 사용한다는 것입니다.

The "this" context

The this context in method is a document instance. Astronomy methods does not execute this.save() automatically, so if you want to save changes made to the document, you have to execute the save() method by yourself.

메소드의이 컨텍스트는 문서 인스턴스입니다. Astronomy 메소드는 this.save()를 자동으로 실행하지 않으므로 문서의 변경 사항을 저장하려면 직접 save() 메소드를 실행해야합니다.

Method invocation object

Sometimes you may need to get method's invocation object, giving you access to such properties like isSimulation, userId or unblock() method which are available in Meteor methods in the this context. To get the current invocation object you have to call DDP._CurrentInvocation.get().

때로는 메서드의 호출 객체를 얻어야 할 수도 있습니다. Meteor 메서드에서와 같이 this 객체에서 isSimulation, userId 또는 unblock() 메서드와 같은 속성에 액세스 할 수 있습니다. 현재 호출 객체를 가져 오려면 DDP._CurrentInvocation.get()을 호출해야합니다.

import { DDP } from 'meteor/ddp-client';
User.extend({
meteorMethods: {
rename(firstName, lastName) {
const invocation = DDP._CurrentInvocation.get();
invocation.isSimulation;
invocation.unblock();
invocation.userId;
/* ... */
},
},
});