Skip to content

Commit

Permalink
#562 Value cache functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
YevgeniyShunevych committed Oct 20, 2021
1 parent 1da15bd commit 770d0bc
Show file tree
Hide file tree
Showing 5 changed files with 323 additions and 41 deletions.
23 changes: 23 additions & 0 deletions src/Atata/Attributes/Caching/UsesValueCacheAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Atata
{
/// <summary>
/// Specifies whether the component value cache mechanic should be used.
/// Caches a value of a component when it is requested at first time,
/// then returns the cached value on further scope requests.
/// </summary>
public class UsesValueCacheAttribute : MulticastAttribute, ICanUseCache
{
public UsesValueCacheAttribute()
: this(true)
{
}

public UsesValueCacheAttribute(bool useCache)
{
UseCache = useCache;
}

/// <inheritdoc/>
public bool UseCache { get; }
}
}
38 changes: 8 additions & 30 deletions src/Atata/Components/ControlList`2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,19 +230,15 @@ protected TItem GetItemByIndex(int index)
TItem DoGetItemByIndex() =>
CreateItem(itemName, new FindByIndexAttribute(index));

return UseScopeCache
? _cachedNamedItemsMap.GetOrAdd(itemName, DoGetItemByIndex)
: DoGetItemByIndex();
return _cachedNamedItemsMap.GetOrAdd(itemName, DoGetItemByIndex);
}

protected TItem GetItemByInnerXPath(string itemName, string xPath)
{
TItem DoGetItemByInnerXPath() =>
CreateItem(itemName, new FindByInnerXPathAttribute(xPath));

return UseScopeCache
? _cachedNamedItemsMap.GetOrAdd(itemName, DoGetItemByInnerXPath)
: DoGetItemByInnerXPath();
return _cachedNamedItemsMap.GetOrAdd(itemName, DoGetItemByInnerXPath);
}

protected virtual TItem GetItem(string name, Expression<Func<TItem, bool>> predicateExpression)
Expand All @@ -258,9 +254,7 @@ TItem DoGetItem()
return CreateItem(scopeLocator, name);
}

return UseScopeCache
? _cachedNamedItemsMap.GetOrAdd(name, DoGetItem)
: DoGetItem();
return _cachedNamedItemsMap.GetOrAdd(name, DoGetItem);
}

/// <summary>
Expand Down Expand Up @@ -315,17 +309,10 @@ protected TItem GetOrCreateItemByElement(IWebElement element, string name)
TItem DoGetOrCreateItemByElement() =>
CreateItem(new DefinedScopeLocator(element), name);

if (UseScopeCache)
{
TItem item = _cachedElementItemsMap.GetOrAdd(element, DoGetOrCreateItemByElement);
item.Metadata.RemoveAll(x => x is NameAttribute);
item.Metadata.Push(new NameAttribute(name));
return item;
}
else
{
return DoGetOrCreateItemByElement();
}
TItem item = _cachedElementItemsMap.GetOrAdd(element, DoGetOrCreateItemByElement);
item.Metadata.RemoveAll(x => x is NameAttribute);
item.Metadata.Push(new NameAttribute(name));
return item;
}

protected virtual TItem CreateItem(string name, params Attribute[] attributes)
Expand Down Expand Up @@ -546,18 +533,14 @@ void IClearsCache.ClearCache() =>
/// <returns>The instance of the owner page object.</returns>
public TOwner ClearCache()
{
bool hasItemsToClear = false;

if (_cachedAllElementsMap.Count > 0)
{
hasItemsToClear = true;
_cachedAllElementsMap.Clear();
Component.Owner.Log.Trace($"Cleared scope cache of {Component.ComponentFullName} {ComponentPartName}");
}

if (_cachedNamedItemsMap.Count > 0)
{
hasItemsToClear = true;

foreach (var item in _cachedNamedItemsMap.Values)
item.ClearCache();

Expand All @@ -566,17 +549,12 @@ public TOwner ClearCache()

if (_cachedElementItemsMap.Count > 0)
{
hasItemsToClear = true;

foreach (var item in _cachedElementItemsMap.Values)
item.ClearCache();

_cachedElementItemsMap.Clear();
}

if (hasItemsToClear)
Component.Owner.Log.Trace($"Cleared scope cache of {Component.ComponentFullName} {ComponentPartName}");

return Component.Owner;
}
}
Expand Down
41 changes: 41 additions & 0 deletions src/Atata/Components/Fields/Field`2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ protected Field()
{
}

protected T CachedValue { get; private set; }

protected bool HasCachedValue { get; private set; }

protected bool UsesValueCache =>
Metadata.Get<ICanUseCache>(filter => filter.Where(x => x is UsesCacheAttribute || x is UsesValueCacheAttribute))
?.UseCache ?? false;

/// <summary>
/// Gets the value.
/// Also executes <see cref="TriggerEvents.BeforeGet"/> and <see cref="TriggerEvents.AfterGet"/> triggers.
Expand Down Expand Up @@ -85,12 +93,21 @@ public static explicit operator T(Field<T, TOwner> field)
/// <returns>The value.</returns>
public T Get()
{
if (HasCachedValue && UsesValueCache)
return CachedValue;

ExecuteTriggers(TriggerEvents.BeforeGet);

T value = GetValue();

ExecuteTriggers(TriggerEvents.AfterGet);

if (UsesValueCache)
{
CachedValue = value;
HasCachedValue = true;
}

return value;
}

Expand Down Expand Up @@ -180,5 +197,29 @@ protected virtual void InitValueTermOptions(TermOptions termOptions, UIComponent
termOptions.Culture = metadata.GetCulture();
termOptions.Format = metadata.GetFormat();
}

protected override void OnClearCache()
{
base.OnClearCache();

ClearValueCache();
}

/// <summary>
/// Clears the column header texts of the component.
/// </summary>
/// <returns>The instance of the owner page object.</returns>
public TOwner ClearValueCache()
{
if (HasCachedValue)
{
var cachedValue = CachedValue;
CachedValue = default;
HasCachedValue = false;
Log.Trace($"Cleared value cache of {ComponentFullName}: {Stringifier.ToString(cachedValue)}");
}

return Owner;
}
}
}
Loading

0 comments on commit 770d0bc

Please sign in to comment.