remote

An article by Gaspard Bucher

Zena::Remote is a library to ease console and other script based work on a Zena application.

introduction

Unlike other ORMs, “Remote” is made to work with multiple connections to Zena applications. This enables migration from different sites and other synchronization features.

connecting to a remote

You simply create a connection to a remote with Remote.connect and authenticate with a secure token:

require 'zena/remote'
app1 = Zena::Remote.connect('http://test.host', 'mytoken')

The connection will not be authorized if the visitor is not in the “api group” of the site (group set in site settings). By default, the “api group” is empty thus forbidding any API access.

get records

There are many ways to find remote records either from a connection or from an already found node.

  • from id
  • from a SQLiss request
  • by querying the indices
  • by doing a fulltext search

All these methods are explained below.

direct id

When you need to find an object from it’s id, you use “first” method with an id:

project = app1.first(44)

QueryBuilder request

This is the most powerful tool because you can execute any SQLiss query. In fact you can implement all other solutions with this method (except the fulltext search).

all

Get a list of records (returns an array or nil):

images = app1.all('images in site')

You can also start from an existing remote node:

project.all('images in project')

Or even use ruby for simple relations:

project.pages

first

Get a single record (returns a Remote::Node or nil):

project = app1.first('project where title like "%shazam%" in site')

Just as “all”, you can start from an existing node:

project.first('image')

or with ruby

project.image

And you can get funky:

app1.find(45).project.pages

index

Here you use the indexed properties of the objects to search. Just as with QueryBuilder based request, you can use “first” or “all”. Use a hash for this type of request:

app1.first(:title => 'shazam')

In fact this is a shortcut for the query:

app1.first("node where title = 'shazam' in site")

You can also use title like '%shazam%' in the latter. See below for another alternative that does this.

fulltext

And finally, you can execute a fulltext query (returning the first 20 entries) with:

app1.search("some text")

root

And finally, there is the special “root” node that can be found with:

app1.root

counting

You can use “count” to get the number of values:

app1.count("contacts in site")

paginate

If you need to paginate entries, or get more then 20 entries with the fulltext query, you can use “per_page” (default to 20) and “page” arguments:

app1.all("contacts in site", :page => 2, :per_page => 15)

create

To create new remote records, you can either use the connection and specify the class as an attribute or first retrieve the remote class and then execute a create:

Use connection

app1.create(:title => "New post", :class => "Post", :parent_id => 54)

Use the remote class

app1['Post'].create(:title => "New post", :parent => some_node)

You may have noticed that whenever you would specify an id or list of ids, you can also provide an instance of a remote node.

delete

You can delete an instance with:

app1.first(454).delete
# or
app1.first(454).destroy

mass delete

You can use the equivalent of an “all” command with “delete”.

For example, the following line destroys all images from the remote site for which the user has “drive” acces (see who can do what).

app1.delete("images in site")

This command first executes a “find” to get all records and then deletes each of them one at a time: there is no “mass” delete API on the server side.

You can also use “destroy” if you prefer the verb.

update

You can update a single instance either with “update_attributes” or save>

app1.first(454).update_attributes(:title => "Fermat's Last Theorem")

or

node = app1.first(454)
node.title = "Fermat's Last Theorem"
node.save

mass update

You can update many objects at once by using executing a query followed by the new attributes.

For example, the following code moves all images from the remote site in a new folder:

folder = app1['Page'].create(:title => 'gallery', :parent => app1.root)
app1.update("images in site", :parent => folder)

remote class

You can access the remote (possibly virtual) class with:

app1['Post']

Once you have a class (instance of Zena::Remote::Klass ), you can use it to find objects of that class (or sub-classes), create objects of this class and get information on defined properties, relations, etc.

You can store the klass instance in a constant if you like:

Post = app1['Post']

You should only use a constant if you are connecting to a single remote server (during a console session for example).

find with a Klass

Here are some examples:

Post = app1['Post']
Post.first
Post.all
Post.all("created_at.year > 2010")
Post.first(:title => "badaboom")

create with a Klass

When you use a remote class, you can create new remote nodes without specifying “class” argument:

app1['Post'].create(:parent => maza, :title => "Hello kitty")

logging

Since some operations can have an important impact on the data (mass update, mass delete), all update/delete operations are logged in “log/hostname.log”. The log contains all the attributes of a deleted node and attribute changes for update operations so that these operations can be reverted to a certain point.

The log does not contain version history, document files, comments, etc.

The format of the logged content is a list of operations encoded with yaml.