Merge lp://qastaging/~rodrigo-moya/couchdb-glib/poll-changes into lp://qastaging/couchdb-glib

Proposed by Rodrigo Moya
Status: Merged
Approved by: Rodrigo Moya
Approved revision: 88
Merge reported by: Rodrigo Moya
Merged at revision: not available
Proposed branch: lp://qastaging/~rodrigo-moya/couchdb-glib/poll-changes
Merge into: lp://qastaging/couchdb-glib
Diff against target: None lines
To merge this branch: bzr merge lp://qastaging/~rodrigo-moya/couchdb-glib/poll-changes
Reviewer Review Type Date Requested Status
dobey (community) Approve
Review via email: mp+10849@code.qastaging.launchpad.net
To post a comment you must log in.
Revision history for this message
Rodrigo Moya (rodrigo-moya) wrote :

Added changes notifications:

* Generate marshallers for CouchDB signals
* Emit modification signals when doing changes to dbs/documents
* Added couchdb_listen_for_changes API
* Added DBWatch object to do the actual watching of changes
* Added tests for modification API and change signals

Revision history for this message
dobey (dobey) wrote :

What's the SONAME convention/policy for couchdb-glib? This changes the ABI I think, since the class structure is larger (added signals), and should probably get a SONAME version bump?

Revision history for this message
Rodrigo Moya (rodrigo-moya) wrote :

> What's the SONAME convention/policy for couchdb-glib? This changes the ABI I
> think, since the class structure is larger (added signals), and should
> probably get a SONAME version bump?

there is no SONAME policy yet, since the ABI/API is still changing almost every week, and since evolution-couchdb is so far the only user of this library, I don't think we want right now to be bumping the soname for every single API/ABI change. I just make sure, when submitting evolution-couchdb packages for karmic, that it asks for the latest couchdb-glib as a build dependency.

Once we are done with this and (maybe) the oAuth stuff, I'll probably start doing the correct soname bumping, but I don't think it's needed right now

Revision history for this message
dobey (dobey) wrote :

OK. The code looks generally OK to me. I just wasn't sure if we were bumping SONAME or not, but if not then I'll go ahead and approve.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure.ac'
2--- configure.ac 2009-08-26 10:47:15 +0000
3+++ configure.ac 2009-08-28 15:44:20 +0000
4@@ -15,6 +15,10 @@
5 AC_PROG_CC
6 AC_PROG_LIBTOOL
7
8+dnl glib-genmarshal
9+AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
10+
11+dnl Look for needed modules
12 PKG_CHECK_MODULES(COUCHDB_GLIB, glib-2.0 gobject-2.0 json-glib-1.0 >= 0.7.4 libsoup-2.4 libsoup-gnome-2.4 uuid)
13 AC_SUBST(COUCHDB_GLIB_CFLAGS)
14 AC_SUBST(COUCHDB_GLIB_LIBS)
15
16=== modified file 'couchdb-glib/Makefile.am'
17--- couchdb-glib/Makefile.am 2009-08-05 14:45:07 +0000
18+++ couchdb-glib/Makefile.am 2009-08-28 15:44:20 +0000
19@@ -3,11 +3,23 @@
20
21 lib_LTLIBRARIES = libcouchdb-glib-1.0.la
22
23+# Marshallers
24+MARSHAL_GENERATED = couchdb-marshal.c couchdb-marshal.h
25+
26+couchdb-marshal.h: couchdb-marshal.list $(GLIB_GENMARSHAL)
27+ $(GLIB_GENMARSHAL) $< --header --prefix=_couchdb_marshal > $@
28+
29+couchdb-marshal.c: couchdb-marshal.list $(GLIB_GENMARSHAL)
30+ $(GLIB_GENMARSHAL) $< --body --prefix=_couchdb_marshal > $@
31+
32 libcouchdb_glib_1_0_la_SOURCES = \
33+ $(MARSHAL_GENERATED) \
34 couchdb.c \
35 couchdb-document.c \
36 couchdb-document-contact.c \
37 couchdb-types.c \
38+ dbwatch.c \
39+ dbwatch.h \
40 utils.c \
41 utils.h
42 libcouchdb_glib_1_0_la_LIBADD = \
43@@ -22,4 +34,6 @@
44 couchdb-document-contact.h \
45 couchdb-types.h
46
47-EXTRA_DIST = $(h_DATA)
48+EXTRA_DIST = $(h_DATA) $(MARSHAL_GENERATED)
49+BUILT_SOURCES = $(MARSHAL_GENERATED)
50+CLEANFILES = $(BUILT_SOURCES)
51
52=== modified file 'couchdb-glib/couchdb-document.c'
53--- couchdb-glib/couchdb-document.c 2009-08-21 15:47:32 +0000
54+++ couchdb-glib/couchdb-document.c 2009-08-28 15:44:20 +0000
55@@ -137,6 +137,11 @@
56 document->dbname = g_strdup (dbname);
57 }
58
59+ if (id)
60+ g_signal_emit_by_name (document->couchdb, "document_updated", dbname, document);
61+ else
62+ g_signal_emit_by_name (document->couchdb, "document_created", dbname, document);
63+
64 result = TRUE;
65 }
66
67@@ -167,7 +172,7 @@
68 if (parser) {
69 g_object_unref (G_OBJECT (parser));
70
71- result = TRUE;
72+ g_signal_emit_by_name (document->couchdb, "document_deleted", document->dbname, id);
73 }
74
75 g_free (url);
76@@ -182,9 +187,11 @@
77
78 if (document->root_node &&
79 json_node_get_node_type (document->root_node) == JSON_NODE_OBJECT) {
80- return json_object_get_string_member (
81- json_node_get_object (document->root_node),
82- "_id");
83+ if (json_object_has_member (json_node_get_object (document->root_node),
84+ "_id"))
85+ return json_object_get_string_member (
86+ json_node_get_object (document->root_node),
87+ "_id");
88 }
89
90 return NULL;
91
92=== modified file 'couchdb-glib/couchdb-glib.h'
93--- couchdb-glib/couchdb-glib.h 2009-08-19 16:43:24 +0000
94+++ couchdb-glib/couchdb-glib.h 2009-08-28 15:44:20 +0000
95@@ -31,9 +31,18 @@
96 #define COUCHDB_IS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COUCHDB_TYPE))
97 #define COUCHDB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), COUCHDB_TYPE, CouchDBClass))
98
99+typedef struct _CouchDBDocument CouchDBDocument;
100+
101 typedef struct _CouchDB CouchDB;
102 typedef struct {
103 GObjectClass parent_class;
104+
105+ void (* database_created) (CouchDB *couchdb, const char *dbname);
106+ void (* database_deleted) (CouchDB *couchdb, const char *dbname);
107+
108+ void (* document_created) (CouchDB *couchdb, const char *dbname, CouchDBDocument *document);
109+ void (* document_updated) (CouchDB *couchdb, const char *dbname, CouchDBDocument *document);
110+ void (* document_deleted) (CouchDB *couchdb, const char *dbname, const char *docid);
111 } CouchDBClass;
112
113 GType couchdb_get_type (void);
114@@ -53,6 +62,8 @@
115 gboolean couchdb_create_database (CouchDB *couchdb, const char *dbname, GError **error);
116 gboolean couchdb_delete_database (CouchDB *couchdb, const char *dbname, GError **error);
117
118+void couchdb_listen_for_changes (CouchDB *couchdb, const char *dbname);
119+
120 /*
121 * Documents API
122 */
123@@ -64,7 +75,6 @@
124 #define COUCHDB_IS_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COUCHDB_TYPE_DOCUMENT))
125 #define COUCHDB_DOCUMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), COUCHDB_TYPE_DOCUMENT, CouchDBDocumentClass))
126
127-typedef struct _CouchDBDocument CouchDBDocument;
128 typedef struct {
129 GObjectClass parent_class;
130 } CouchDBDocumentClass;
131
132=== added file 'couchdb-glib/couchdb-marshal.list'
133--- couchdb-glib/couchdb-marshal.list 1970-01-01 00:00:00 +0000
134+++ couchdb-glib/couchdb-marshal.list 2009-08-28 15:44:20 +0000
135@@ -0,0 +1,2 @@
136+NONE:STRING,OBJECT
137+NONE:STRING,STRING
138
139=== modified file 'couchdb-glib/couchdb.c'
140--- couchdb-glib/couchdb.c 2009-08-13 10:59:59 +0000
141+++ couchdb-glib/couchdb.c 2009-08-28 15:44:20 +0000
142@@ -23,15 +23,29 @@
143 #include <libsoup/soup-gnome.h>
144 #include <json-glib/json-glib.h>
145 #include "couchdb-glib.h"
146+#include "couchdb-marshal.h"
147+#include "dbwatch.h"
148 #include "utils.h"
149
150 G_DEFINE_TYPE(CouchDB, couchdb, G_TYPE_OBJECT)
151
152+enum {
153+ DATABASE_CREATED,
154+ DATABASE_DELETED,
155+ DOCUMENT_CREATED,
156+ DOCUMENT_UPDATED,
157+ DOCUMENT_DELETED,
158+ LAST_SIGNAL
159+};
160+static guint couchdb_signals[LAST_SIGNAL];
161+
162 static void
163 couchdb_finalize (GObject *object)
164 {
165 CouchDB *couchdb = COUCHDB (object);
166
167+ g_hash_table_destroy (couchdb->db_watchlist);
168+
169 g_free (couchdb->hostname);
170 g_object_unref (couchdb->http_session);
171
172@@ -44,11 +58,64 @@
173 GObjectClass *object_class = G_OBJECT_CLASS (klass);
174
175 object_class->finalize = couchdb_finalize;
176+
177+ /* Signals */
178+ couchdb_signals[DATABASE_CREATED] =
179+ g_signal_new ("database_created",
180+ G_OBJECT_CLASS_TYPE (object_class),
181+ G_SIGNAL_RUN_LAST,
182+ G_STRUCT_OFFSET (CouchDBClass, database_created),
183+ NULL, NULL,
184+ g_cclosure_marshal_VOID__STRING,
185+ G_TYPE_NONE, 1,
186+ G_TYPE_STRING);
187+ couchdb_signals[DATABASE_DELETED] =
188+ g_signal_new ("database_deleted",
189+ G_OBJECT_CLASS_TYPE (object_class),
190+ G_SIGNAL_RUN_LAST,
191+ G_STRUCT_OFFSET (CouchDBClass, database_deleted),
192+ NULL, NULL,
193+ g_cclosure_marshal_VOID__STRING,
194+ G_TYPE_NONE, 1,
195+ G_TYPE_STRING);
196+ couchdb_signals[DOCUMENT_CREATED] =
197+ g_signal_new ("document_created",
198+ G_OBJECT_CLASS_TYPE (object_class),
199+ G_SIGNAL_RUN_LAST,
200+ G_STRUCT_OFFSET (CouchDBClass, document_created),
201+ NULL, NULL,
202+ _couchdb_marshal_VOID__STRING_OBJECT,
203+ G_TYPE_NONE, 2,
204+ G_TYPE_STRING,
205+ G_TYPE_OBJECT);
206+ couchdb_signals[DOCUMENT_UPDATED] =
207+ g_signal_new ("document_updated",
208+ G_OBJECT_CLASS_TYPE (object_class),
209+ G_SIGNAL_RUN_LAST,
210+ G_STRUCT_OFFSET (CouchDBClass, document_updated),
211+ NULL, NULL,
212+ _couchdb_marshal_VOID__STRING_OBJECT,
213+ G_TYPE_NONE, 2,
214+ G_TYPE_STRING,
215+ G_TYPE_OBJECT);
216+ couchdb_signals[DOCUMENT_DELETED] =
217+ g_signal_new ("document_deleted",
218+ G_OBJECT_CLASS_TYPE (object_class),
219+ G_SIGNAL_RUN_LAST,
220+ G_STRUCT_OFFSET (CouchDBClass, document_deleted),
221+ NULL, NULL,
222+ _couchdb_marshal_VOID__STRING_STRING,
223+ G_TYPE_NONE, 2,
224+ G_TYPE_STRING,
225+ G_TYPE_STRING);
226 }
227
228 static void
229 couchdb_init (CouchDB *couchdb)
230 {
231+ couchdb->db_watchlist = g_hash_table_new_full (g_str_hash, g_str_equal,
232+ (GDestroyNotify) g_free,
233+ (GDestroyNotify) dbwatch_free);
234 }
235
236 CouchDB *
237@@ -168,6 +235,9 @@
238
239 g_free (url);
240
241+ if (result)
242+ g_signal_emit_by_name (couchdb, "database_created", dbname);
243+
244 return result;
245 }
246
247@@ -196,6 +266,14 @@
248
249 g_free (url);
250
251+ if (result) {
252+ /* If we're listening for changes on this database, stop doing so */
253+ if (g_hash_table_lookup (couchdb->db_watchlist, dbname))
254+ g_hash_table_remove (couchdb->db_watchlist, dbname);
255+
256+ g_signal_emit_by_name (couchdb, "database_deleted", dbname);
257+ }
258+
259 return result;
260 }
261
262@@ -261,3 +339,39 @@
263 g_slist_foreach (doclist, (GFunc) couchdb_document_info_unref, NULL);
264 g_slist_free (doclist);
265 }
266+
267+void
268+couchdb_listen_for_changes (CouchDB *couchdb, const char *dbname)
269+{
270+ DBWatch *watch;
271+ CouchDBDatabaseInfo *db_info;
272+ GError *error = NULL;
273+
274+ g_return_if_fail (COUCHDB_IS (couchdb));
275+ g_return_if_fail (dbname != NULL);
276+
277+ watch = g_hash_table_lookup (couchdb->db_watchlist, dbname);
278+ if (watch) {
279+ g_warning ("Already listening for changes in '%s' database", dbname);
280+ return;
281+ }
282+
283+ /* Retrieve information for database, to know the last_update_sequence */
284+ db_info = couchdb_get_database_info (couchdb, dbname, &error);
285+ if (!db_info) {
286+ g_warning ("Could not retrieve information for '%s' database: %s",
287+ dbname, error->message);
288+ g_error_free (error);
289+
290+ return;
291+ }
292+
293+ watch = dbwatch_new (couchdb,
294+ dbname,
295+ couchdb_database_info_get_update_sequence (db_info));
296+ if (watch)
297+ g_hash_table_insert (couchdb->db_watchlist, g_strdup (dbname), watch);
298+
299+ /* Free memory */
300+ couchdb_database_info_unref (db_info);
301+}
302
303=== added file 'couchdb-glib/dbwatch.c'
304--- couchdb-glib/dbwatch.c 1970-01-01 00:00:00 +0000
305+++ couchdb-glib/dbwatch.c 2009-08-28 15:44:20 +0000
306@@ -0,0 +1,137 @@
307+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
308+/*
309+ * Copyright (C) 2009 Canonical Services Ltd (www.canonical.com)
310+ *
311+ * Authors: Rodrigo Moya <rodrigo.moya@canonical.com>
312+ *
313+ * This library is free software; you can redistribute it and/or
314+ * modify it under the terms of version 2 of the GNU Lesser General Public
315+ * License as published by the Free Software Foundation.
316+ *
317+ * This program is distributed in the hope that it will be useful,
318+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
319+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
320+ * General Public License for more details.
321+ *
322+ * You should have received a copy of the GNU Lesser General Public
323+ * License along with this library; if not, write to the
324+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
325+ * Boston, MA 02110-1301, USA.
326+ */
327+
328+#include "couchdb-glib.h"
329+#include "dbwatch.h"
330+#include "utils.h"
331+
332+#define TIMEOUT_SECONDS 300
333+
334+static void
335+process_change (DBWatch *watch, JsonNode *node)
336+{
337+ JsonObject *this_change;
338+ const gchar *id;
339+ CouchDBDocument *document;
340+ GError *error = NULL;
341+
342+ if (json_node_get_node_type (node) != JSON_NODE_OBJECT)
343+ return;
344+
345+ this_change = json_node_get_object (node);
346+ if (!json_object_has_member (this_change, "id"))
347+ return;
348+
349+ id = json_object_get_string_member (this_change, "id");
350+
351+ /* We need to try retrieving the document, to check if it's removed or not */
352+ document = couchdb_document_get (watch->couchdb, watch->dbname, id, &error);
353+ if (document) {
354+ const gchar *revision;
355+
356+ revision = couchdb_document_get_revision (document);
357+ if (revision != NULL) {
358+ if (revision[0] == '1')
359+ g_signal_emit_by_name (watch->couchdb, "document_created",
360+ watch->dbname, document);
361+ else
362+ g_signal_emit_by_name (watch->couchdb, "document_updated",
363+ watch->dbname, document);
364+ }
365+
366+ g_object_unref (G_OBJECT (document));
367+ } else {
368+ if (error != NULL) {
369+ g_warning ("Error retrieving document '%s': %s", id, error->message);
370+ g_error_free (error);
371+ } else {
372+ /* The document is no longer in the DB, notify */
373+ g_signal_emit_by_name (watch->couchdb, "document_deleted", watch->dbname, id);
374+ }
375+ }
376+}
377+
378+static gboolean
379+watch_timeout_cb (gpointer user_data)
380+{
381+ char *url;
382+ JsonParser *parser;
383+ GError *error = NULL;
384+ DBWatch *watch = (DBWatch *) user_data;
385+
386+ url = g_strdup_printf ("%s/%s/_changes?since=%d",
387+ watch->couchdb->hostname,
388+ watch->dbname,
389+ watch->last_update_seq);
390+ parser = send_message_and_parse (watch->couchdb, SOUP_METHOD_GET, url, NULL, &error);
391+ if (parser != NULL) {
392+ JsonNode *root_node;
393+
394+ root_node = json_parser_get_root (parser);
395+ if (json_node_get_node_type (root_node) == JSON_NODE_OBJECT) {
396+ JsonObject *root_object;
397+ JsonArray *results;
398+
399+ root_object = json_node_get_object (root_node);
400+ results = json_object_get_array_member (root_object, "results");
401+ if (results) {
402+ GList *json_elements, *sl;
403+
404+ json_elements = json_array_get_elements (results);
405+ for (sl = json_elements; sl != NULL; sl = sl->next)
406+ process_change (watch, (JsonNode *) sl->data);
407+ }
408+
409+ if (json_object_has_member (root_object, "last_seq"))
410+ watch->last_update_seq = json_object_get_int_member (root_object, "last_seq");
411+ }
412+
413+ g_object_unref (G_OBJECT (parser));
414+ }
415+
416+ /* Free memory */
417+ g_free (url);
418+}
419+
420+DBWatch *
421+dbwatch_new (CouchDB *couchdb, const gchar *dbname, gint update_seq)
422+{
423+ DBWatch *watch;
424+
425+ watch = g_new0 (DBWatch, 1);
426+ watch->couchdb = couchdb;
427+ watch->dbname = g_strdup (dbname);
428+ watch->last_update_seq = update_seq;
429+
430+ /* Set timeout to check for changes every 5 minutes*/
431+ watch->timeout_id = g_timeout_add (TIMEOUT_SECONDS * 1000, (GSourceFunc) watch_timeout_cb, watch);
432+
433+ return watch;
434+}
435+
436+void
437+dbwatch_free (DBWatch *watch)
438+{
439+ g_free (watch->dbname);
440+ g_source_remove (watch->timeout_id);
441+
442+ g_free (watch);
443+}
444
445=== added file 'couchdb-glib/dbwatch.h'
446--- couchdb-glib/dbwatch.h 1970-01-01 00:00:00 +0000
447+++ couchdb-glib/dbwatch.h 2009-08-28 15:44:20 +0000
448@@ -0,0 +1,37 @@
449+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
450+/*
451+ * Copyright (C) 2009 Canonical Services Ltd (www.canonical.com)
452+ *
453+ * Authors: Rodrigo Moya <rodrigo.moya@canonical.com>
454+ *
455+ * This library is free software; you can redistribute it and/or
456+ * modify it under the terms of version 2 of the GNU Lesser General Public
457+ * License as published by the Free Software Foundation.
458+ *
459+ * This program is distributed in the hope that it will be useful,
460+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
461+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
462+ * General Public License for more details.
463+ *
464+ * You should have received a copy of the GNU Lesser General Public
465+ * License along with this library; if not, write to the
466+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
467+ * Boston, MA 02110-1301, USA.
468+ */
469+
470+#ifndef __DBWATCH_H__
471+#define __DBWATCH_H__
472+
473+#include "utils.h"
474+
475+typedef struct {
476+ CouchDB *couchdb;
477+ gchar *dbname;
478+ gint last_update_seq;
479+ guint timeout_id;
480+} DBWatch;
481+
482+DBWatch *dbwatch_new (CouchDB *couchdb, const gchar *dbname, gint update_seq);
483+void dbwatch_free (DBWatch *watch);
484+
485+#endif
486
487=== modified file 'couchdb-glib/utils.h'
488--- couchdb-glib/utils.h 2009-08-17 22:26:51 +0000
489+++ couchdb-glib/utils.h 2009-08-28 15:44:20 +0000
490@@ -30,6 +30,8 @@
491
492 char *hostname;
493 SoupSession *http_session;
494+
495+ GHashTable *db_watchlist;
496 };
497
498 struct _CouchDBDocument {
499
500=== modified file 'tests/test-couchdb-glib.c'
501--- tests/test-couchdb-glib.c 2009-07-27 21:49:15 +0000
502+++ tests/test-couchdb-glib.c 2009-08-28 15:44:20 +0000
503@@ -20,6 +20,7 @@
504 */
505
506 #include <couchdb-glib.h>
507+#include <utils.h>
508
509 static CouchDB *couchdb;
510
511@@ -47,7 +48,8 @@
512 doclist = couchdb_list_documents (couchdb, (const char *) dblist->data, &error);
513 g_assert (error == NULL);
514 g_assert (g_slist_length (doclist) == couchdb_database_info_get_documents_count (dbinfo));
515- couchdb_free_document_list (doclist);
516+ if (doclist)
517+ couchdb_free_document_list (doclist);
518
519 dblist = g_slist_remove (dblist, dblist->data);
520 couchdb_database_info_unref (dbinfo);
521@@ -98,6 +100,82 @@
522 }
523 }
524
525+static void
526+test_change_databases (void)
527+{
528+ char *dbname;
529+ gint i;
530+ GError *error = NULL;
531+
532+ dbname = generate_uuid ();
533+ g_assert (dbname != NULL);
534+
535+ /* Create database */
536+ couchdb_create_database (couchdb, dbname, &error);
537+ g_assert (error == NULL);
538+
539+ couchdb_listen_for_changes (couchdb, dbname);
540+
541+ /* Create some documents */
542+ for (i = 0; i < 10; i++) {
543+ CouchDBDocument *document;
544+ char *str;
545+
546+ document = couchdb_document_new (couchdb);
547+ g_assert (document != NULL);
548+
549+ couchdb_document_set_boolean_field (document, "boolean", TRUE);
550+ couchdb_document_set_int_field (document, "int", i);
551+ couchdb_document_set_double_field (document, "double", (gdouble) i);
552+
553+ str = g_strdup_printf ("value%d", i);
554+ couchdb_document_set_string_field (document, "string", str);
555+ g_free (str);
556+
557+ g_assert (couchdb_document_put (document, dbname, &error));
558+ g_assert (error == NULL);
559+ }
560+
561+ /* Delete database */
562+ g_assert (couchdb_delete_database (couchdb, dbname, &error));
563+ g_assert (error == NULL);
564+
565+ /* Free memory */
566+ g_free (dbname);
567+}
568+
569+static void
570+db_created_cb (CouchDB *couchdb, const char *dbname, gpointer user_data)
571+{
572+ g_print ("Database %s has been created\n", dbname);
573+}
574+
575+static void
576+db_deleted_cb (CouchDB *couchdb, const char *dbname, gpointer user_data)
577+{
578+ g_print ("Database %s has been deleted\n", dbname);
579+}
580+
581+static void
582+doc_changed_cb (CouchDB *couchdb, const char *dbname, CouchDBDocument *document, gpointer user_data)
583+{
584+ char *doc_str;
585+
586+ doc_str = couchdb_document_to_string (document);
587+ g_print ("Document %s has been %s: %s\n",
588+ couchdb_document_get_id (document),
589+ (const gchar *) user_data,
590+ doc_str);
591+
592+ g_free (doc_str);
593+}
594+
595+static void
596+doc_deleted_cb (CouchDB *couchdb, const char *dbname, const char *docid, gpointer user_data)
597+{
598+ g_print ("Document %s in database %s has been deleted\n", docid, dbname);
599+}
600+
601 int
602 main (int argc, char *argv[])
603 {
604@@ -112,9 +190,16 @@
605 return -1;
606 }
607
608+ g_signal_connect (G_OBJECT (couchdb), "database_created", G_CALLBACK (db_created_cb), NULL);
609+ g_signal_connect (G_OBJECT (couchdb), "database_deleted", G_CALLBACK (db_deleted_cb), NULL);
610+ g_signal_connect (G_OBJECT (couchdb), "document_created", G_CALLBACK (doc_changed_cb), "created");
611+ g_signal_connect (G_OBJECT (couchdb), "document_updated", G_CALLBACK (doc_changed_cb), "updated");
612+ g_signal_connect (G_OBJECT (couchdb), "document_deleted", G_CALLBACK (doc_deleted_cb), NULL);
613+
614 /* Setup test functions */
615 g_test_add_func ("/testcouchdbglib/ListDatabases", test_list_databases);
616 g_test_add_func ("/testcouchdbglib/ListDocuments", test_list_documents);
617+ g_test_add_func ("/testcouchdbglib/ChangeDatabases", test_change_databases);
618
619 return g_test_run ();
620 }

Subscribers

People subscribed via source and target branches