       }
 
       # Searches for a rule to handle it.
-      rule = @_rules[@_dispatch.call item]
+      ruleName = @_dispatch.call item
+      rule = @_rules[ruleName]
       @_assert(
         rule?
-        "undefined rule “#{rule}”"
+        "undefined rule “#{ruleName}”"
       )
 
+      # Checks if this is a singleton.
+      @_assert(
+        not rule.singleton
+        "cannot add items to singleton rule “#{rule.name}”"
+      )
+
       # Computes its key.
-      key = rule.key item
+      key = rule.key.call item
+
+      @_assert(
+        $_.isString key
+        "the key “#{key}” is not a string"
+      )
 
       if key in @_byKey
         # Marks this item as not to be removed.

    # Removes any items not seen (iff `remove` is true).
    @_removeItem item for _, item of itemsToRemove

  #--------------------------------

  _assert: (cond, message) ->

    return unless @_runHook 'beforeUpdate', item

    # Computes its value.
    do ->
      # Item is not passed directly to function to avoid direct
      # modification.
      #
      # This is not a true security but better than nothing.
      proxy = Object.create item

      updateValue = (parent, prop, def) ->
        if not $_.isObject def
          parent[prop] = def
        else if $_.isFunction def
          parent[prop] = def.call proxy
        else if $_.isArray def
          i = 0
          n = def.length

          current = parent[prop] ?= new Array n
          while i < n
            updateValue current, i, def[i]
            ++i
        else
          # It's a plain object.
          current = parent[prop] ?= {}
          for i of def
            updateValue current, i, def[i]

      updateValue item, 'val', rule.val

    return unless @_runHook 'beforeSave', item

    # Registers the new item.
    @_byKey[item.key] = item

    # Emits events.
    @emit "key: #{item.key}", item
    @emit "rule: #{rule}", item

    # TODO: checks for loops.

  _removeItem: (item) ->

    delete @_byKey[item.key]

    # TODO: Cascades changes.

#=====================================================================

module.exports = $MappedCollection2
