assets_set.rb 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. # The name AssetsSet combines the Assets term in Zammad with the Set class from the Ruby StdLib.
  3. # Zammad Assets serve the purpose to limit requests to the REST API. For a requested object all
  4. # related and relevant objects are collected recursively and then send to the client in one go.
  5. # A Ruby Set implements a collection of unordered values with no duplicates.
  6. #
  7. # This class implements a collection of Zammad Assets with no duplicates.
  8. # This is done by having two structures:
  9. #
  10. # 1st: The actual Assets Hash (e.g. `assets[model_name][object_id] = object_attributes`)
  11. # 2nd: A lookup table for keeping track which elements were added to the actual Assets Hash
  12. #
  13. # The actual Assets Hash should be flushed after sending it to the client. This will keep the
  14. # lookup table untouched. The next time a object that was already send to the client
  15. # should be added to the actual Assets Hash the lookup table will recognize the object
  16. # and will prevent the addition to the actual Assets Hash.
  17. # This way Assets will be send only once to the client for the lifetime of a AssetsSet instance.
  18. class AssetsSet < SimpleDelegator
  19. # This method overwrites the SimpleDelegator initializer
  20. # to be able to have the actual Assets Hash as an optional argument.
  21. def initialize(assets = {})
  22. super(assets)
  23. end
  24. # This method initializes the the global lookup table.
  25. # Each (accessed) Model gets it's own sub structure in it.
  26. def lookup_table
  27. @lookup_table ||= {}
  28. end
  29. # This method flushes the actual Assets Hash.
  30. def flush
  31. __setobj__({})
  32. end
  33. # This method intercepts `assets[model_name]` calls by registering a AssetsSet::Proxy.
  34. # Instead of creating an entry in the actual Assets Hash a AssetsSet::Proxy.
  35. # See AssetsSet::Proxy for further information.
  36. # Existing proxies will be reused.
  37. def [](model)
  38. __getobj__[model] ||= proxy(model)
  39. end
  40. # This method is used to convert the AssetsSet into a regular Hash which then can be send to the client.
  41. # It cleans up empty Model sub structures in the internal structure.
  42. def to_h
  43. super.compact_blank!.transform_values!(&:to_h)
  44. end
  45. private
  46. def proxy(model)
  47. lookup_table[model] ||= {}
  48. ::AssetsSet::Proxy.new.tap do |proxy|
  49. proxy.lookup_table = lookup_table[model]
  50. end
  51. end
  52. end