diff --git a/src/djangoflash/models.py b/src/djangoflash/models.py index d8dc05a..f5089c6 100644 --- a/src/djangoflash/models.py +++ b/src/djangoflash/models.py @@ -22,39 +22,43 @@ class FlashScope(object): The following operations are supported by :class:`FlashScope` instances: - .. describe:: len(f) + .. describe:: len(flash) - Returns the number of items in the flash *f*. + Returns the number of items in the *flash*. - .. describe:: f[key] + .. describe:: flash[key] - Returns the item of *f* with key *key*. Raises a :exc:`KeyError` if - *key* is not in the flash *f*. + Returns the item of *flash* with key *key*. Raises a :exc:`KeyError` if + *key* is not in the *flash*. - .. describe:: f[key] = value + .. describe:: flash[key] = value - Sets ``f[key]`` to *value*. + Sets ``flash[key]`` to *value*. - .. describe:: del f[key] + .. describe:: del flash[key] - Removes ``f[key]`` from *f*. Raises a :exc:`KeyError` if *key* is not - in the map. + Removes ``flash[key]``. Raises a :exc:`KeyError` if *key* is not in the + *flash*. - .. describe:: key in f + .. describe:: key in flash - Returns ``True`` if *f* has a key *key*, else ``False``. + Returns ``True`` if *flash* has a key *key*, else ``False``. - .. describe:: key not in f + .. describe:: key not in flash - Equivalent to ``not key in f``. + Equivalent to ``not key in flash``. - .. describe:: f.now[key] = value + .. describe:: flash.now[key] = value - Sets ``f[key]`` to *value* and marks it as *used*. + Sets ``flash[key]`` to *value* and marks it as *used*. - .. describe:: f.now(**items) + .. describe:: flash.now(**items) - Puts the given *items* into *f* and marks them as *used*. + Puts the given *items* into *flash* and marks them as *used*. + + .. describe:: flash.now.add(key, *values) + + Appends one or more *values* to *key* in the *flash*. """ def __init__(self, data=None): @@ -92,6 +96,12 @@ def __delitem__(self, key): if key in self._used: del self._used[key] + def __call__(self, **kwargs): + """Puts one or more values into this flash. + """ + for key, value in kwargs.items(): + self[key] = value + def __len__(self): """Returns the number of values inside this flash. """ @@ -163,31 +173,28 @@ def pop(self, key, default=None): del self._used[key] return value - def has_key(self, key): - """Returns ``True`` if there's a value under the given *key*. - - .. deprecated:: 1.4.2 - :meth:`has_key()` is deprecated in favor of ``key in f``. - """ - return self._session.has_key(key) - def put(self, **kwargs): """Puts one or more values into this flash. + + .. deprecated :: 1.7.1 + :meth:`put` is deprecated in favor of ``flash(key=value)``. """ for key, value in kwargs.items(): self[key] = value - def add(self, key, value): - """Appends a value to a key in this flash. + def add(self, key, *values): + """Appends one or more *values* to *key* in this flash. """ if key in self: current_value = self[key] if not isinstance(current_value, list): - self[key] = [current_value, value] + self[key] = [current_value] + self[key].extend(values) else: - current_value.append(value) + current_value.extend(values) + self[key] = current_value else: - self[key] = [value] + self[key] = list(values) def clear(self): """Removes all items from this flash. @@ -197,6 +204,9 @@ def clear(self): def put_immediate(self, key, value): """Puts a value inside this flash and marks it as *used*. + + .. deprecated :: 1.7.1 + :meth:`put_immediate` is deprecated in favor of ``flash.now[key] = value``. """ self[key] = value self._update_status(key) @@ -278,14 +288,19 @@ def __contains__(self, key): return key in self.delegate def __setitem__(self, key, value): - """Puts a *value* that *won't* be available to the next request, only to - the current. + """Puts a *value* into this flash under the given *key*. """ - self.delegate.put_immediate(key, value) + self.delegate[key] = value + self.delegate.discard(key) def __call__(self, **kwargs): - """Puts values that *won't* be available to the next request, only to - the current. + """Puts one or more values into this flash. """ for key, value in kwargs.items(): self[key] = value + + def add(self, key, *values): + """Appends one or more values to a key in this flash. + """ + self.delegate.add(key, *values) + self.delegate.discard(key) diff --git a/src/djangoflash/tests/models.py b/src/djangoflash/tests/models.py index 36fb373..119b1e3 100644 --- a/src/djangoflash/tests/models.py +++ b/src/djangoflash/tests/models.py @@ -69,11 +69,10 @@ def test_contains(self): self.assertFalse('error' in self.flash) self.assertEqual('Info', self.flash['info']) - def test_get_item(self): - """FlashScope: flash[key]" syntax should be supported. + def test_get_invalid_item(self): + """FlashScope: Should raise KeyError if trying to get an invalid value. """ self.assertRaises(KeyError, lambda: self.flash['error']); - self.assertEqual('Info', self.flash['info']); def test_set_item(self): """FlashScope: flash[key] = value" syntax should be supported. @@ -138,24 +137,36 @@ def test_iteritems(self): self.assertRaises(StopIteration, iterator.next) def test_add_with_existing_non_list_value(self): - """FlashScope: Should add values to a key even if the current value is not a list. + """FlashScope: Should append a value to a key even if the current value is not a list. """ self.flash.add('info', 'Error') self.assertEqual(['Info', 'Error'], self.flash['info']) def test_add_with_existing_list_value(self): - """FlashScope: Should add a new value if the current value is a list. + """FlashScope: Should append a value if the current value is a list. """ self.flash['error'] = ['Error 1'] self.flash.add('error', 'Error 2') self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) - def test_add_with_no_existing_value(self): + def test_add_with_non_existing_value(self): """FlashScope: Should add a value even if the given key doesn't exists. """ self.flash.add('error', 'Error') self.assertEqual(['Error'], self.flash['error']) + def test_add_across_requests(self): + """FlashScope: Should keep a key when a value is appended to it. + """ + self.flash['error'] = 'Error 1' + self.flash.update() + self.flash.add('error', 'Error 2') + self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) + self.flash.update() + self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) + self.flash.update() + self.assertFalse('error' in self.flash) + def test_get(self): """FlashScope: Should return a default value if the given key doesn' exists. """ @@ -164,41 +175,42 @@ def test_get(self): self.assertEqual(None, self.flash.get('error')) def test_pop(self): - """FlashScope: Should pop an item from the flash scope. + """FlashScope: Should pop a value from the flash scope. """ self.assertEqual(None, self.flash.pop('error')) self.assertEqual('Info', self.flash.pop('info')) self.assertFalse('info' in self.flash) def test_pop_used_value(self): - """FlashScope: Should pop an used item from the flash scope. + """FlashScope: Should pop a used value from the flash scope. """ self.flash.update() self.assertEqual('Info', self.flash.pop('info')) self.assertFalse('info' in self.flash) - def test_has_key(self): - """FlashScope: Should check if there's a value related with the given key. - """ - self.assertFalse(self.flash.has_key('error')) - self.assertTrue(self.flash.has_key('info')) - def test_put(self): - """FlashScope: Should put several values into the flash scope at the same time. + """FlashScope: Should put several keys into the flash scope at the same time. """ self.flash.put(warn='Warning', error='Error') self.assertEqual('Warning', self.flash['warn']) self.assertEqual('Error', self.flash['error']) + def test_new_put(self): + """FlashScope: Should put several keys into the flash scope at the same time (new way). + """ + self.flash(warn='Warning', error='Error') + self.assertEqual('Warning', self.flash['warn']) + self.assertEqual('Error', self.flash['error']) + def test_discard(self): - """FlashScope: Should mark a flash-scoped value for removal. + """FlashScope: Should mark a value for removal. """ self.flash.discard() self.flash.update() self.assertFalse('info' in self.flash) def test_keep(self): - """FlashScope: Should avoid the removal of specific flash-scoped values. + """FlashScope: Should avoid the removal of specific values. """ self.flash.update() self.flash.keep('info') @@ -208,7 +220,7 @@ def test_keep(self): self.assertFalse('info' in self.flash) def test_keep_all(self): - """FlashScope: Should avoid the removal of all flash-scoped values. + """FlashScope: Should avoid the removal of all values. """ self.flash.update() self.flash.keep() @@ -217,37 +229,8 @@ def test_keep_all(self): self.flash.update() self.assertFalse('info' in self.flash) - def test_alternative_now(self): - """FlashScope: Immediate values (flash.now) should be supported. - """ - self.flash.now(error='Error') - self.assertEqual('Error', self.flash['error']) - self.flash.update() - self.assertFalse('error' in self.flash) - - def test_now(self): - """FlashScope: "flash.now[key] = value" syntax should be supported. - """ - self.flash.now['error'] = 'Error' - self.assertEqual('Error', self.flash['error']) - self.flash.update() - self.assertFalse('error' in self.flash) - - def test_now_get_item(self): - """FlashScope: "FlashScope: "flash.now[key]" syntax should be supported. - """ - self.flash.now['error'] = 'Error' - self.assertEqual('Error', self.flash.now['error']) - - def test_now_contains(self): - """FlashScope: "key in flash.now" syntax should be supported. - """ - self.assertFalse('error' in self.flash.now) - self.flash.now['error'] = 'Error' - self.assertTrue('error' in self.flash.now) - - def test_replace_value(self): - """FlashScope: Should replace a value properly. + def test_replace_used_value(self): + """FlashScope: Should keep a key when its value is replaced. """ self.flash.update() self.assertEqual('Info', self.flash['info']) @@ -258,16 +241,6 @@ def test_replace_value(self): self.flash.update() self.assertFalse('info' in self.flash) - def test_replace_with_immediage_value(self): - """FlashScope: Should replace a value properly by a immediate value. - """ - self.flash.update() - self.assertEqual('Info', self.flash['info']) - self.flash.now['info'] = 'Error' - self.assertEqual('Error', self.flash['info']) - self.flash.update() - self.assertFalse('info' in self.flash) - def test_empty_to_dict(self): """FlashScope: Should export the flash data to a dict even if it's empty. """ @@ -293,3 +266,133 @@ def test_to_dict_immutability(self): data = self.flash.to_dict() del self.flash['info'] self.assertEqual('Info', data[_SESSION_KEY]['info']) + + +class ImmediateFlashScope(TestCase): + """Tests the ``Flashscope.now``. + """ + def setUp(self): + """Create a FlashScope object to be used by the test methods. + """ + self.flash = FlashScope() + self.flash.now['info'] = 'Info' + + def test_now(self): + """FlashScope.now: "flash.now[key] = value" syntax should be supported. + """ + self.assertEqual('Info', self.flash['info']) + self.flash.update() + self.assertFalse('info' in self.flash) + + def test_alternative_now(self): + """FlashScope.now: Immediate values (flash.now) should be supported. + """ + self.flash.now(error='Error') + self.assertEqual('Error', self.flash['error']) + self.flash.update() + self.assertFalse('error' in self.flash) + + def test_contains(self): + """FlashScope.now: "key in flash.now" syntax should be supported. + """ + self.assertFalse('error' in self.flash.now) + self.flash.now['error'] = 'Error' + self.assertTrue('error' in self.flash.now) + + def test_get_invalid_item(self): + """FlashScope.now: Should raise KeyError if trying to get an invalid item. + """ + self.assertRaises(KeyError, lambda: self.flash.now['error']); + + def test_add_with_non_existing_value(self): + """FlashScope.now: Should append an immediate value even if the given key doesn't exists. + """ + self.flash.now.add('error', 'Error 1') + self.flash.now.add('error', 'Error 2', 'Error 3') + self.assertEqual(['Error 1', 'Error 2', 'Error 3'], self.flash['error']) + + def test_add_with_existing_non_list_value(self): + """FlashScope.now: Should append immediate values to a key even if the current value is not a list. + """ + self.flash.now.add('info', 'Error 1') + self.flash.now.add('info', 'Error 2', 'Error 3') + self.assertEqual(['Info', 'Error 1', 'Error 2', 'Error 3'], self.flash['info']) + + def test_add_with_existing_list_value(self): + """FlashScope.now: Should append an immediate value if the current value is a list. + """ + self.flash.now['error'] = ['Error 1'] + self.flash.now.add('error', 'Error 2') + self.flash.now.add('error', 'Error 3', 'Error 4') + self.assertEqual(['Error 1', 'Error 2', 'Error 3', 'Error 4'], self.flash['error']) + + +class MixedFlashScope(TestCase): + """Tests mixing regular and immediate values. + """ + def setUp(self): + """Create a FlashScope object to be used by the test methods. + """ + self.flash = FlashScope() + + def test_replace_with_immediate_value(self): + """FlashScope: Should replace a regular value by an immediate value. + """ + self.flash['info'] = 'Info' + self.flash.update() + self.assertEqual('Info', self.flash['info']) + self.flash.now['info'] = 'Error' + self.assertEqual('Error', self.flash['info']) + self.flash.update() + self.assertFalse('info' in self.flash) + + def test_replace_immediate_with_regular_value(self): + """FlashScope: Should replace an immediate value with a regular value. + """ + self.flash.now['info'] = 'Info' + self.assertEqual('Info', self.flash['info']) + self.flash['info'] = 'Error' + self.flash.update() + self.assertEqual('Error', self.flash['info']) + self.flash.update() + self.assertFalse('info' in self.flash) + + def test_add_immediate_with_existing_regular_value(self): + """FlashScope.now: Should add an immediate value to a regular key, expiring on the current request. + """ + self.flash['error'] = 'Error 1' + self.flash.now.add('error', 'Error 2') + self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) + self.flash.update() + self.assertFalse('error' in self.flash) + + def test_add_immediate_with_existing_regular_list(self): + """FlashScope.now: Should add an immediate value to a regular list, expiring on the current request. + """ + self.flash['error'] = ['Error 1'] + self.flash.now.add('error', 'Error 2') + self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) + self.flash.update() + self.assertFalse('error' in self.flash) + + def test_add_regular_with_existing_immediate_value(self): + """FlashScope: Should add a regular value to an immediate key, expiring on the next request. + """ + self.flash.now['error'] = 'Error 1' + self.flash.add('error', 'Error 2') + self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) + self.flash.update() + self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) + self.flash.update() + self.assertFalse('error' in self.flash) + + def test_add_regular_with_existing_immediate_list(self): + """FlashScope: Should add a regular value to an immediate list, expiring on the next request. + """ + self.flash.now['error'] = ['Error 1'] + self.flash.add('error', 'Error 2') + self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) + self.flash.update() + self.assertEqual(['Error 1', 'Error 2'], self.flash['error']) + self.flash.update() + self.assertFalse('error' in self.flash)