Return-Path: X-Original-To: apmail-couchdb-dev-archive@www.apache.org Delivered-To: apmail-couchdb-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 3E53611000 for ; Thu, 8 Aug 2013 12:05:56 +0000 (UTC) Received: (qmail 56662 invoked by uid 500); 8 Aug 2013 12:05:55 -0000 Delivered-To: apmail-couchdb-dev-archive@couchdb.apache.org Received: (qmail 56239 invoked by uid 500); 8 Aug 2013 12:05:53 -0000 Mailing-List: contact dev-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list dev@couchdb.apache.org Received: (qmail 56212 invoked by uid 99); 8 Aug 2013 12:05:51 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 08 Aug 2013 12:05:51 +0000 X-ASF-Spam-Status: No, hits=1.5 required=5.0 tests=HTML_MESSAGE,RCVD_IN_DNSWL_LOW,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (nike.apache.org: domain of jason.h.smith@gmail.com designates 209.85.219.50 as permitted sender) Received: from [209.85.219.50] (HELO mail-oa0-f50.google.com) (209.85.219.50) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 08 Aug 2013 12:05:44 +0000 Received: by mail-oa0-f50.google.com with SMTP id i4so5252944oah.9 for ; Thu, 08 Aug 2013 05:05:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:sender:in-reply-to:references:from:date:message-id :subject:to:content-type; bh=ka6W1pxe1HSYIwNJe+RDOXbLfdgnOcbOi8+IzYpAYsU=; b=v8hjk1/Hq1E2SmPMusugP2FrNAs1DLLpnUvJVBV7W3n2fZz9ocXevVB+9smKkgIaN2 14gHDy+ycNyuDt2LpyrduHM3NKdORE2LDrmmdMh4lEDlD+D5vONmupXeTYoxS5rkh6oY 4kdRHcbFotjFR7dMVQPViopRYUOofHCpOzgnPXhwM4QQrp2jHoGraVjrmuyLiH36e1iS 1+4BsIDgUEPyqXMSDzVdc+TMQUpzvAhLzbu5hXgz7eIWCuMWFS5h1vvr3sQxLDTXDP4R Mqp1MqE/Rd51mH8yVJFwNjTdgqFZgiZ0zM6FW/Qm8VnbsiDSZla6gup87LvVSxIVRZfU H5bg== X-Received: by 10.60.38.199 with SMTP id i7mr4020534oek.36.1375963523617; Thu, 08 Aug 2013 05:05:23 -0700 (PDT) MIME-Version: 1.0 Sender: jason.h.smith@gmail.com Received: by 10.182.103.133 with HTTP; Thu, 8 Aug 2013 05:05:03 -0700 (PDT) In-Reply-To: <617A17F9-7349-4D07-A689-95CD6CE2D6D3@apache.org> References: <96FBCC7A-F696-4BD8-9B7B-4126F3B5362A@apache.org> <001BB5DC-23F7-4BEB-9553-09DCF4F9F5CB@apache.org> <617A17F9-7349-4D07-A689-95CD6CE2D6D3@apache.org> From: Jason Smith Date: Thu, 8 Aug 2013 19:05:03 +0700 X-Google-Sender-Auth: _T33s5C_1ZSFT2RBlyeKSrui4p0 Message-ID: Subject: Re: [PLUGINS] Plugin Hooks To: "dev@couchdb.apache.org" Content-Type: multipart/alternative; boundary=089e014951b60c98e404e36e794d X-Virus-Checked: Checked by ClamAV on apache.org --089e014951b60c98e404e36e794d Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Note, I intentially began this discussion as a question, not a statement. Totally thinking "aloud." I think it is like gen_server. For gen_server all you need is a handle_call/3 and handle_cast/2. The real API is inside that. For example, maybe all that's required for the behaviour is on/1 and we can make a flexible hook system from that. (Now why would "on" be a meaningful or useful function name?) on({log, Severity, Message}) -> ok , io:format("Couch says ~s\n", [Message]) ; % Suppose I want a database that stays synced to my config % (something I personally need at the moment). on({startup}) -> ok , io:format("CouchDB started!\n") , init_my_config_db(couch_config:get("*/*")) % Pseudocode ; on({config, Section, Key, Value}) -> ok , io:format("Config update: ~s/~s =3D ~p\n", [Section, Key, Value]) , update_my_config_db(Section, Key, Value) ; on(Unknown) -> ok , io:format("Did you know there is a ~p hook?\n", [element(1, Unknown)]= ) . On Thu, Aug 8, 2013 at 5:42 PM, Jan Lehnardt wrote: > > On Aug 8, 2013, at 12:39 , Jason Smith wrote: > > > Well, I just googled it. Basically there is a couchdb_plugin.erl which > > tells Erlang what a behavior looks like. And all that does is define th= e > > functions and arity which a couchdb_plugin would have to export. > > > > Probably there are some better Erlangers on the list who might chime in= . > It > > looks like okay bang-for-buck; only not much bang or much buck. > > how would this work if a plugin is only interested in handling a single > hook, > would it have to implement mock funs for all hooks then? > > > On Thu, Aug 8, 2013 at 5:26 PM, Jan Lehnardt wrote: > > > >> how would this look in code? > >> > >> On Aug 8, 2013, at 12:21 , Jason Smith wrote: > >> > >>> Perhaps a custom behaviour to help catch API problems at compile time= ? > >>> > >>> -behaviour(couchdb_plugin). > >>> > >>> > >>> > >>> On Thu, Aug 8, 2013 at 3:47 PM, Jan Lehnardt wrote: > >>> > >>>> Heya, > >>>> > >>>> I=E2=80=99m toying with the idea of moving some of my experimental i= nto > >>>> bona-fide plugins. One of them is my log_to_db branch that on top of > >>>> writing log messages to a text file also writes a document to a log > >>>> database. > >>>> > >>>> Conceptually, this is the perfect use of a plugin: the feature is no= t > >>>> useful in the general case, because *any* activity creates write loa= d > >>>> on a single database, but for certain low-volume installations, this > >>>> might be a useful feature (I wouldn=E2=80=99t have written it, if I = hadn=E2=80=99t > >>>> needed it at some point) so allowing users to enable it as a plugin > >>>> would be nice. > >>>> > >>>> But regardless of whether my plugin is useful, it illustrates an > >>>> interesting point: > >>>> > >>>> A log_to_db plugin would need to register for logging events or, if = it > >>>> doesn=E2=80=99t want to duplicate all the logging-level logic in cou= ch_log, it > >>>> would need some way of injecting a function call into > >>>> `couch_log:log().`. We could of course try and find a way where a > >>>> plugin would be able to provide an API compatible version of a Couch= DB > >>>> module and swap it out for it=E2=80=99s custom one, but that=E2=80= =99s hardly a great > >>>> idea. > >>>> > >>>> Other software has the notion of =E2=80=9Chooks=E2=80=9D (some may c= all it something > >>>> else) where at well defined points in the main code base, external > >>>> functions get called with certain parameters. To make things dynamic= , > >>>> there might be a way for plugins to register to be called by those > >>>> hooks and the main code then asks the registry whether there are any > >>>> plugin functions to call. > >>>> > >>>> In the log_to_db example, we=E2=80=99d have something like this: > >>>> > >>>> couch_log_to_db.erl: > >>>> > >>>> init() -> > >>>> couch_hooks:register(couch_log_hook, log_hook_fun/1), > >>>> ok. > >>>> > >>>> log_hook_fun(Log) -> > >>>> % do the log_to_db magic > >>>> ok. > >>>> > >>>> > >>>> couch_hooks.erl: > >>>> > >>>> register(Hook, Fun) -> > >>>> % store the Fun with the Hook somewhere > >>>> ok. > >>>> > >>>> call(Hook, Args) -> > >>>> % retrieve Fun for Hook from somewhere > >>>> Fun(Args). > >>>> > >>>> couch_log.erl: > >>>> > >>>> % in log() > >>>> > >>>> ... > >>>> couch_hooks:call(couch_log_hook, Args), > >>>> ... > >>>> > >>>> The main code would define what the hook name and arguments are and > the > >>>> plugin would have to conform. The plugin registry would just manage > the > >>>> registration and calling of functions for a hook, but nothing more. > >>>> > >>>> * * * > >>>> > >>>> This is just my first stab at this not thinking about it too much an= d > I > >>>> likely miss some subtleties in Erlang that make this not work (hot > code > >>>> upgrades e.g.). > >>>> > >>>> > >>>> How do you think we should implement a hooks feature in CouchDB? > >>>> > >>>> > >>>> Thanks! > >>>> Jan > >>>> -- > >>>> > >>>> > >>>> > >>>> > >>>> > >> > >> > > --089e014951b60c98e404e36e794d--