Skip to content

Commit d3b49ff

Browse files
committed
Introduced new IsDirty() overloads for AttributeValueCollection
Previously, there was an `IsDirty()` method that accepted an attribute key (`name`) to check the status of an individual attribute. In most use cases, though, it makes more sense to check the entire `AttributeValueCollection` to see if there are _any_ dirty attributes, as that's a condition that will determine if the topic itself should be saved. To support this, a new overload is introduced which, absent any parameters, will return true of any `AttribtueValue`s stored in it are marked as `IsDirty`. In addition, however, this presents an opportunity to centralize logic for identifying extraneous byline (`LastModifiedBy`) and dateline (`LastModified`) attributes. These attribute values are automatically and dynamically generated by some clients, such as the **OnTopic Editor**. As a result, if you save a topic five times without making any changes, you'll end up with five versions due to the `DateModified` being automatically updated. To help address that in a centralized fashion, the `IsDirty()` method also accepts an `excludeLastModified` parameter which will only report `true` if there are `AttributeValue`s marked as `IsDirty` whose `Key`s don't start with `LastModified`. This provides a useful condition for excluding extraneous updates—and, therefore, "orphaned" versions. This supports Remove byline and dateline if no other attributes are updated (#20).
1 parent 8b7dffe commit d3b49ff

File tree

2 files changed

+86
-2
lines changed

2 files changed

+86
-2
lines changed

OnTopic.Tests/AttributeValueCollectionTest.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using OnTopic.Attributes;
99
using OnTopic.Collections;
1010
using Microsoft.VisualStudio.TestTools.UnitTesting;
11+
using System.Globalization;
1112

1213
namespace OnTopic.Tests {
1314

@@ -251,6 +252,65 @@ public void SetValue_ValueUnchanged_IsNotDirty() {
251252

252253
}
253254

255+
/*==========================================================================================================================
256+
| TEST: IS DIRTY: DIRTY VALUES: RETURNS TRUE
257+
\-------------------------------------------------------------------------------------------------------------------------*/
258+
/// <summary>
259+
/// Populates the <see cref="AttributeValueCollection"/> with a <see cref="AttributeValue"/> that is marked as <see
260+
/// cref="AttributeValue.IsDirty"/>. Confirms that <see cref="AttributeValueCollection.IsDirty(Boolean)"/> returns
261+
/// <c>true</c>/
262+
/// </summary>
263+
[TestMethod]
264+
public void IsDirty_DirtyValues_ReturnsTrue() {
265+
266+
var topic = TopicFactory.Create("Test", "Container");
267+
268+
topic.Attributes.SetValue("Foo", "Bar");
269+
270+
Assert.IsTrue(topic.Attributes.IsDirty());
271+
272+
}
273+
274+
/*==========================================================================================================================
275+
| TEST: IS DIRTY: NO DIRTY VALUES: RETURNS FALSE
276+
\-------------------------------------------------------------------------------------------------------------------------*/
277+
/// <summary>
278+
/// Populates the <see cref="AttributeValueCollection"/> with a <see cref="AttributeValue"/> that is <i>not</i> marked as
279+
/// <see cref="AttributeValue.IsDirty"/>. Confirms that <see cref="AttributeValueCollection.IsDirty(Boolean)"/> returns
280+
/// <c>false</c>/
281+
/// </summary>
282+
[TestMethod]
283+
public void IsDirty_NoDirtyValues_ReturnsFalse() {
284+
285+
var topic = TopicFactory.Create("Test", "Container", 1);
286+
287+
topic.Attributes.SetValue("Foo", "Bar", false);
288+
289+
Assert.IsFalse(topic.Attributes.IsDirty());
290+
291+
}
292+
293+
/*==========================================================================================================================
294+
| TEST: IS DIRTY: EXCLUDE LAST MODIFIED: RETURNS FALSE
295+
\-------------------------------------------------------------------------------------------------------------------------*/
296+
/// <summary>
297+
/// Populates the <see cref="AttributeValueCollection"/> with a <see cref="AttributeValue"/> that is <i>not</i> marked as
298+
/// <see cref="AttributeValue.IsDirty"/> as well as a <c>LastModified</c> <see cref="AttributeValue"/> that is. Confirms
299+
/// that <see cref="AttributeValueCollection.IsDirty(Boolean)"/> returns <c>false</c>.
300+
/// </summary>
301+
[TestMethod]
302+
public void IsDirty_ExcludeLastModified_ReturnsFalse() {
303+
304+
var topic = TopicFactory.Create("Test", "Container", 1);
305+
306+
topic.Attributes.SetValue("Foo", "Bar", false);
307+
topic.Attributes.SetValue("LastModified", DateTime.Now.ToString(CultureInfo.InvariantCulture));
308+
topic.Attributes.SetValue("LastModifiedBy", "System");
309+
310+
Assert.IsFalse(topic.Attributes.IsDirty(true));
311+
312+
}
313+
254314
/*==========================================================================================================================
255315
| TEST: SET VALUE: INVALID VALUE: THROWS EXCEPTION
256316
\-------------------------------------------------------------------------------------------------------------------------*/

OnTopic/Collections/AttributeValueCollection.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System;
77
using System.Collections.ObjectModel;
88
using System.Diagnostics.CodeAnalysis;
9+
using System.Linq;
910
using OnTopic.Attributes;
1011
using OnTopic.Internal.Diagnostics;
1112
using OnTopic.Internal.Reflection;
@@ -54,14 +55,37 @@ internal AttributeValueCollection(Topic parentTopic) : base(StringComparer.Invar
5455
/*==========================================================================================================================
5556
| METHOD: IS DIRTY
5657
\-------------------------------------------------------------------------------------------------------------------------*/
58+
/// <summary>
59+
/// Determine if <i>any</i> attributes in the <see cref="AttributeValueCollection"/> are dirty.
60+
/// </summary>
61+
/// <remarks>
62+
/// This method is intended primarily for data storage providers, such as
63+
/// <see cref="Repositories.ITopicRepository"/>, which may need to determine if any attributes are dirty prior to saving
64+
/// them to the data storage medium. Be aware that this does <i>not</i> track any <see cref="AttributeValue"/>s that may
65+
/// have been <i>deleted</i>, nor whether any <see cref="Topic.Relationships"/> have been modified; as such, it may still
66+
/// be necessary to persist changes to the storage medium.
67+
/// </remarks>
68+
/// <param name="excludeLastModified">
69+
/// Optionally excludes <see cref="AttributeValue"/>s whose keys start with <c>LastModified</c>. This is useful for
70+
/// excluding the byline (<c>LastModifiedBy</c>) and dateline (<c>LastModified</c>) since these values are automatically
71+
/// generated by e.g. the OnTopic Editor and, thus, may be irrelevant updates if no other attribute values have changed.
72+
/// </param>
73+
/// <returns>True if the attribute value is marked as dirty; otherwise false.</returns>
74+
public bool IsDirty(bool excludeLastModified = false)
75+
=> Items.Any(a =>
76+
a.IsDirty &&
77+
(!excludeLastModified || !a.Key.StartsWith("LastModified", StringComparison.InvariantCultureIgnoreCase))
78+
);
79+
5780
/// <summary>
5881
/// Determine if a given attribute is marked as dirty. Will return false if the attribute key cannot be found.
5982
/// </summary>
6083
/// <remarks>
6184
/// This method is intended primarily for data storage providers, such as
6285
/// <see cref="OnTopic.Repositories.ITopicRepository"/>, which may need to determine if a specific attribute key is
63-
/// dirty prior to saving it to the data storage medium. Because isDirty is a state of the current attribute value, it
64-
/// does not support inheritFromParent or inheritFromDerived (which otherwise default to true).
86+
/// dirty prior to saving it to the data storage medium. Because <c>IsDirty</c> is a state of the current <see
87+
/// cref="AttributeValue"/>, it does not support <c>inheritFromParent</c> or <c>inheritFromDerived</c> (which otherwise
88+
/// default to <c>true</c>).
6589
/// </remarks>
6690
/// <param name="name">The string identifier for the <see cref="AttributeValue"/>.</param>
6791
/// <returns>True if the attribute value is marked as dirty; otherwise false.</returns>

0 commit comments

Comments
 (0)