# Path: feedplatform/hooks.py
# _DEFAULT_HOOKS = [
#     # Called before a feed is downloaded and
#     # parsed. Gets passed the feed db object
#     # and a parser argument dict. Can return
#     # True if further processing should be
#     # stopped. Can modify the feedparser
#     # arguments.
#     'before_parse',
# 
#     # Called after a feed was successfully
#     # retrieved and parsed, though note that it
#     # still may be bozo. Gets passed the feed
#     # db object, as well as the parsed data dict.
#     # Can return True if further processing
#     # should be stopped.
#     # To collect feed-wide data, this is the
#     # right place. TODO: or is this better
#     # done in a separate hook (say 'feed',
#     # 'handle_feed'/'while_feed'/'do_feed', 'process_feed'?
#     # depends on how error handling is going to work.
#     'after_parse',
# 
#     # The first thing that runs for every entry in
#     # a feed; can return True to stop processing
#     # of this entry.
#     'item',
# 
#     # Determine item guid BEFORE the default
#     # <guid> tag is chosen.
#     'get_guid',
# 
#     # Determine item guid AFTER <guid> was
#     # found missing missing - provided that
#     # ``get_guid`` did not return a match.
#     'need_guid',
# 
#     # No guid was found by any of the addins.
#     # At this point, it is already determined
#     # that the item will be skipped, but you
#     # may add further handling beyond that.
#     'no_guid',
# 
#     # Try to find an existing item for the
#     # guid determined. This runs BEFORE the
#     # default attempt to find the item.
#     'get_item',
# 
#     # Try to find an item for the guid after
#     # both ``get_item`` and the default code
#     # failed.
#     'need_item',
# 
#     # A new item was found in a feed, and now
#     # needs to be initialized. This allows to
#     # customize instantiation of the Item model,
#     # e.g. by using a subclass.
#     # Note that you are fully responsible of
#     # initializing the instance, e.g. hooking
#     # it up to the parent feed, and setting
#     # the guid.
#     'create_item',
# 
#     # Follows 'create_item' after a new item was
#     # found in a feed, but the Item instance is
#     # created already (though not yet flushed).
#     # See also found_item, which is usually
#     # combined with this to update item metadata.
#     'new_item',
# 
#     # Basically the opposing hook to new_item.
#     # This gets called if an item in a feed was
#     # detected to be already in the database.
#     # Usually combined with new_item to update
#     # item metadata.
#     'found_item',
# 
#     # Called when an item is processed, both for
#     # new and existing items. The ``created``
#     # argument let's you distinguish.
#     # Note the difference between this an
#     # new_item/found_item, and the reason the latter
#     # two exist: in new_item, the item does not yet
#     # have a primary key, but it does here. If
#     # you need a primary key, for example for
#     # associating information in a separate model,
#     # and put your code into new_item instead, you
#     # would cause an implicit flush() for the item,
#     # which in combination with other addins following
#     # that change the instance too could lead two two
#     # flushes()/two queries, instead of only one:
#     #       unflushed item
#     #       process_item hook causes implicit flush() -> query!
#     #       process_item hook changes item
#     #       default flush() on changed item           -> query!
#     # XXX: Test this situation.
#     #
#     # The lowdown: If you don't need a primary key,
#     # hook into new_item/found_item instead.
#     'process_item',
# 
#     # Dummy test hook. Never actually called,
#     # provides no useful functionality. Ignore.
#     'alien_invasion',
# ]
# SUPPORTED_HOOKS = None
# _CALLBACKS = None
#     _CALLBACKS = {}
#     SUPPORTED_HOOKS = copy.copy(_DEFAULT_HOOKS)
# def reset():
# def add_callback(name, func, priority=0):
# def any(name):
# def trigger(name, args=[], kwargs={}, all=False):
# def _validate_hook_name(name):
# def register(name):
# def exists(name):


from nose.tools import assert_raises
from feedplatform import hooks

    # fifo: without a priority, the callback added first is called first
    hooks.reset()
    hooks.add_callback('alien_invasion', lambda: 1)
    hooks.add_callback('alien_invasion', lambda: 2)
    hooks.add_callback('alien_invasion', lambda: 3)
    assert hooks.trigger('alien_invasion') == 1

    # but callback priorization works as well
    hooks.reset()
    hooks.add_callback('alien_invasion', lambda: 'p10', priority=10)
    hooks.add_callback('alien_invasion', lambda: 'p20', priority=20)
    hooks.add_callback('alien_invasion', lambda: 'p5', priority=5)
    assert hooks.trigger('alien_invasion') == 'p20'


def test_custom():
    """Test custom, non-default hooks.
    """

    # this fails, hook doesn't yet exist
    assert_raises(Exception, hooks.add_callback, 'i_love_you', lambda: None)
    assert_raises(Exception, hooks.trigger, 'i_love_you')

    # after we register the hook, it works
    hooks.register('i_love_you')
    hooks.add_callback('i_love_you', lambda: None)
    hooks.trigger('i_love_you')

    # registering the same hook multiple times is a no-op
    hooks.register('i_love_you')

# Path: feedplatform/hooks.py
# _DEFAULT_HOOKS = [
#     # Called before a feed is downloaded and
#     # parsed. Gets passed the feed db object
#     # and a parser argument dict. Can return
#     # True if further processing should be
#     # stopped. Can modify the feedparser
#     # arguments.
#     'before_parse',
# 
#     # Called after a feed was successfully
#     # retrieved and parsed, though note that it
#     # still may be bozo. Gets passed the feed
#     # db object, as well as the parsed data dict.
#     # Can return True if further processing
#     # should be stopped.
#     # To collect feed-wide data, this is the
#     # right place. TODO: or is this better
#     # done in a separate hook (say 'feed',
#     # 'handle_feed'/'while_feed'/'do_feed', 'process_feed'?
#     # depends on how error handling is going to work.
#     'after_parse',
# 
#     # The first thing that runs for every entry in
#     # a feed; can return True to stop processing
#     # of this entry.
#     'item',
# 
#     # Determine item guid BEFORE the default
#     # <guid> tag is chosen.
#     'get_guid',
# 
#     # Determine item guid AFTER <guid> was
#     # found missing missing - provided that
#     # ``get_guid`` did not return a match.
#     'need_guid',
# 
#     # No guid was found by any of the addins.
#     # At this point, it is already determined
#     # that the item will be skipped, but you
#     # may add further handling beyond that.
#     'no_guid',
# 
#     # Try to find an existing item for the
#     # guid determined. This runs BEFORE the
#     # default attempt to find the item.
#     'get_item',
# 
#     # Try to find an item for the guid after
#     # both ``get_item`` and the default code
#     # failed.
#     'need_item',
# 
#     # A new item was found in a feed, and now
#     # needs to be initialized. This allows to
#     # customize instantiation of the Item model,
#     # e.g. by using a subclass.
#     # Note that you are fully responsible of
#     # initializing the instance, e.g. hooking
#     # it up to the parent feed, and setting
#     # the guid.
#     'create_item',
# 
#     # Follows 'create_item' after a new item was
#     # found in a feed, but the Item instance is
#     # created already (though not yet flushed).
#     # See also found_item, which is usually
#     # combined with this to update item metadata.
#     'new_item',
# 
#     # Basically the opposing hook to new_item.
#     # This gets called if an item in a feed was
#     # detected to be already in the database.
#     # Usually combined with new_item to update
#     # item metadata.
#     'found_item',
# 
#     # Called when an item is processed, both for
#     # new and existing items. The ``created``
#     # argument let's you distinguish.
#     # Note the difference between this an
#     # new_item/found_item, and the reason the latter
#     # two exist: in new_item, the item does not yet
#     # have a primary key, but it does here. If
#     # you need a primary key, for example for
#     # associating information in a separate model,
#     # and put your code into new_item instead, you
#     # would cause an implicit flush() for the item,
#     # which in combination with other addins following
#     # that change the instance too could lead two two
#     # flushes()/two queries, instead of only one:
#     #       unflushed item
#     #       process_item hook causes implicit flush() -> query!
#     #       process_item hook changes item
#     #       default flush() on changed item           -> query!
#     # XXX: Test this situation.
#     #
#     # The lowdown: If you don't need a primary key,
#     # hook into new_item/found_item instead.
#     'process_item',
# 
#     # Dummy test hook. Never actually called,
#     # provides no useful functionality. Ignore.
#     'alien_invasion',
# ]
# SUPPORTED_HOOKS = None
# _CALLBACKS = None
#     _CALLBACKS = {}
#     SUPPORTED_HOOKS = copy.copy(_DEFAULT_HOOKS)
# def reset():
# def add_callback(name, func, priority=0):
# def any(name):
# def trigger(name, args=[], kwargs={}, all=False):
# def _validate_hook_name(name):
# def register(name):
# def exists(name):


# Path: tests/hooks/test_basic.py
from nose.tools import assert_raises
from feedplatform import hooks

    # fifo: without a priority, the callback added first is called first
    hooks.reset()
    hooks.add_callback('alien_invasion', lambda: 1)
    hooks.add_callback('alien_invasion', lambda: 2)
    hooks.add_callback('alien_invasion', lambda: 3)
    assert hooks.trigger('alien_invasion') == 1

    # but callback priorization works as well
    hooks.reset()
    hooks.add_callback('alien_invasion', lambda: 'p10', priority=10)
    hooks.add_callback('alien_invasion', lambda: 'p20', priority=20)
    hooks.add_callback('alien_invasion', lambda: 'p5', priority=5)
    assert hooks.trigger('alien_invasion') == 'p20'


def test_custom():
    """Test custom, non-default hooks.
    """

    # this fails, hook doesn't yet exist
    assert_raises(Exception, hooks.add_callback, 'i_love_you', lambda: None)
    assert_raises(Exception, hooks.trigger, 'i_love_you')

    # after we register the hook, it works
    hooks.register('i_love_you')
    hooks.add_callback('i_love_you', lambda: None)
    hooks.trigger('i_love_you')

    # registering the same hook multiple times is a no-op
    hooks.register('i_love_you')

    hooks.register('i_love_you')
