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 = ~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 <j...@apache.org> wrote: > > On Aug 8, 2013, at 12:39 , Jason Smith <j...@apache.org> 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 the > > 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 <j...@apache.org> wrote: > > > >> how would this look in code? > >> > >> On Aug 8, 2013, at 12:21 , Jason Smith <j...@apache.org> 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 <j...@apache.org> wrote: > >>> > >>>> Heya, > >>>> > >>>> I’m toying with the idea of moving some of my experimental into > >>>> 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 not > >>>> useful in the general case, because *any* activity creates write load > >>>> on a single database, but for certain low-volume installations, this > >>>> might be a useful feature (I wouldn’t have written it, if I hadn’t > >>>> 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’t want to duplicate all the logging-level logic in couch_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 CouchDB > >>>> module and swap it out for it’s custom one, but that’s hardly a great > >>>> idea. > >>>> > >>>> Other software has the notion of “hooks” (some may call 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’d 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 and > 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 > >>>> -- > >>>> > >>>> > >>>> > >>>> > >>>> > >> > >> > >