Module: Ecoportal::API::Common::Content::DoubleModel::Attributable::Nesting::CascadedCallback::InstanceMethods

Defined in:
lib/ecoportal/api/common/content/double_model/attributable/nesting/cascaded_callback.rb

Overview

Note:

this happens at instance level (not tracked at class level)

We offer helpers to implement cascaded callbacks to nested objects i.e. #as_update or #dirty?

Instance Method Summary collapse

Instance Method Details

#cascaded_callback(value = nil, method:, args: [], kargs: {}, path: [], recurs: true) {|result, value, key_path, object| ... } ⇒ Variant

Note:

Be aware that

  1. It does NOT natively discard root? objects. You will need to explicitly do this in the block callback.
  2. If method calls in turn to cascaded_callback, to prevent double-recursion you MUST use recurs: false. The problem with this approach is that key_path from the original object that called cascaded_callback is lost. For which you should probably rather use cascaded_reduce.
  3. It already does a first call on self.method, might it respond to. And it will not yield in this first call.
Note:

the cascaded_callback will end were:

  1. The current object does NOT respond to method.
  2. The current object does NOT have nested properties. So where the object does not have any _cascaded_attributes.

Method to go through all the embedded properties recursively.

Yields:

  • (result, value, key_path, object)

    block to accumulate result. The block is yielded for the main (root) object, as well as per each one of its _cascaded_attributes (so nested properties).

Yield Parameters:

  • result (Hash, Variant)

    the accumulated value of calling method across all nested models from root to this point (object).

  • value (Variant)

    the value of calling method on object.

  • key_path (Array<String>)

    the Hash model path of keys to get from root to objecct. It is important to note that an empty path means we are on the top object from where this method was callled.

  • object (DoubleModel)

    the current object.

Yield Returns:

  • (Variant)

    the accumulated result or the current iteration.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/ecoportal/api/common/content/double_model/attributable/nesting/cascaded_callback.rb', line 95

def cascaded_callback(
  value = nil,
  method:,
  args:   [],
  kargs:  {},
  path:   [],
  recurs: true,
  &block
)
  method = method.to_sym
  return value unless respond_to?(method, true)

  value =
    if path.empty?
      # Don't yield on the first call

      send(method, *args, **kargs)
    else
      yield(
        value,
        send(method, *args, **kargs),
        path,
        self
      )
    end

  return value unless respond_to?(:_cascaded_attributes)

  _cascaded_attributes.reduce(value) do |val, (attribute, obj_k)|
    next val unless (obj = send(attribute))
    next val unless obj.respond_to(method, true)

    obj_path = path.dup.push(obj_k)
    # next val if obj_path.empty?


    unless obj.respond_to?(:cascaded_callback)
      next yield(
        val,
        obj.send(method, *args, **kargs),
        obj_path,
        obj
      )
    end

    next val unless recurs

    obj.cascaded_callback(
      val,
      method: method,
      args:   args,
      kargs:  kargs,
      path:   obj_path,
      recurs: recurs,
      &block
    )
  end
end

#cascaded_reduce(init = nil, recurs: false, trace: nil, &block) ⇒ Object

Trace = Struct.new(:subject, :nested_attrs, :obj_key_path)



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/ecoportal/api/common/content/double_model/attributable/nesting/cascaded_callback.rb', line 153

def cascaded_reduce(init = nil, recurs: false, trace: nil, &block)
  trace ||= {}
  trace   = _cascaded_attributes_trace(recurs: recurs) if trace.empty?

  return init if trace.empty?

  params = i[subject key key_path attributes]
  subject, key, key_path, attributes = trace.values_at(*params)

  value = yield(init, subject, key, key_path, trace)
  return value if attributes.nil? || attributes.empty?

  attributes.reduce(value) do |mem, (_attr, subtrace)|
    cascaded_reduce(mem, recurs: recurs, trace: subtrace, &block)
  end
end