Class: Volt::ArrayModel

Inherits:
ReactiveArray show all
Includes:
ModelHelpers, ModelWrapper, StateHelpers, StateManager
Defined in:
lib/volt/models/array_model.rb

Direct Known Subclasses

Cursor

Constant Summary

Constants included from ModelHelpers

ModelHelpers::ID_CHARS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from StateHelpers

#loaded?, #loaded_state

Methods included from StateManager

#change_state_to

Methods included from ModelHelpers

#deep_unwrap, #event_added, #event_removed, #generate_id, included, #self_attributes, #setup_persistor

Methods included from ModelWrapper

#wrap_value, #wrap_values

Methods inherited from ReactiveArray

#==, #[], #[]=, #all?, #any?, #clear, #count, #delete_at, #each, #empty?, #insert, #method_missing, #size

Methods included from Eventable

#on, #remove_listener, #trigger!

Constructor Details

#initialize(array = [], options = {}) ⇒ ArrayModel



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/volt/models/array_model.rb', line 60

def initialize(array = [], options = {})
  @options   = options
  @parent    = options[:parent]
  @path      = options[:path] || []
  @persistor = setup_persistor(options[:persistor])

  array = wrap_values(array)

  super(array)

  if @persistor
    @persistor.loaded
  else
    change_state_to(:loaded_state, :loaded, false)
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Volt::ReactiveArray

Instance Attribute Details

#arrayObject (readonly)

Returns the value of attribute array.



17
18
19
# File 'lib/volt/models/array_model.rb', line 17

def array
  @array
end

#optionsObject (readonly)

Returns the value of attribute options.



17
18
19
# File 'lib/volt/models/array_model.rb', line 17

def options
  @options
end

#parentObject (readonly)

Returns the value of attribute parent.



17
18
19
# File 'lib/volt/models/array_model.rb', line 17

def parent
  @parent
end

#pathObject (readonly)

Returns the value of attribute path.



17
18
19
# File 'lib/volt/models/array_model.rb', line 17

def path
  @path
end

#persistorObject (readonly)

Returns the value of attribute persistor.



17
18
19
# File 'lib/volt/models/array_model.rb', line 17

def persistor
  @persistor
end

Class Method Details

.proxy_to_persistor(*method_names) ⇒ Object

Some methods get passed down to the persistor.



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/volt/models/array_model.rb', line 46

def self.proxy_to_persistor(*method_names)
  method_names.each do |method_name|
    define_method(method_name) do |*args, &block|
      if @persistor.respond_to?(method_name)
        @persistor.send(method_name, *args, &block)
      else
        fail "this model's persistance layer does not support #{method_name}, try using store"
      end
    end
  end
end

.proxy_with_load(*method_names) ⇒ Object

For many methods, we want to register a dependency on the root_dep as soon as the method is called, so it can begin loading. Also, some persistors need to have special loading logic (such as returning a promise instead of immediately returning). To accomplish this, we call the #run_once_loaded method on the persistor.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/volt/models/array_model.rb', line 24

def self.proxy_with_load(*method_names)
  method_names.each do |method_name|
    old_method_name = :"__old_#{method_name}"
    alias_method(old_method_name, method_name)

    define_method(method_name) do |*args, &block|
      # track on the root dep
      persistor.try(:root_dep).try(:depend)

      if persistor.respond_to?(:run_once_loaded) &&
          !Volt.in_mode?(:no_model_promises)
        persistor.run_once_loaded(block) do
          send(old_method_name, *args)
        end
      else
        send(old_method_name, *args)
      end
    end
  end
end

Instance Method Details

#+(*args) ⇒ Object

Make sure it gets wrapped



239
240
241
242
# File 'lib/volt/models/array_model.rb', line 239

def +(*args)
  args = wrap_values(args)
  super(*args)
end

#<<(model) ⇒ Object

Make sure it gets wrapped



89
90
91
92
93
94
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
# File 'lib/volt/models/array_model.rb', line 89

def <<(model)
  if model.is_a?(Model)
    # Set the new path and the persistor.
    model.options = @options.merge(path: @options[:path] + [:[]])
  else
    model = wrap_values([model]).first
  end

  if model.is_a?(Model) && !model.can_create?
    fail "permissions did not allow create for #{model.inspect}"
  end

  # Add it to the array and trigger any watches or on events.
  super(model)

  if @persistor
    promise = @persistor.added(model, @array.size - 1)
    if promise && promise.is_a?(Promise)
      return promise.then do

        # Mark the model as not new
        model.instance_variable_set('@new', false)

        # Mark the model as loaded
        model.change_state_to(:loaded_state, :loaded)

        # return the model
        model
      end.fail do |err|
        # remove from the collection because it failed to save on the server
        # we don't need to call delete on the server.
        index = @array.index(model)
        delete_at(index, true)

        # re-raise, err might not be an Error object, so we use a rejected promise to re-raise
        Promise.new.reject(err)
      end
    end
  end

  # Return this model
  Promise.new.resolve(model)
end

#allObject

Return the model, on store, .all is proxied to wait for load and return a promise.



206
207
208
# File 'lib/volt/models/array_model.rb', line 206

def all
  self
end

#append(model) ⇒ Object

Works like << except it always returns a promise



134
135
136
137
138
139
# File 'lib/volt/models/array_model.rb', line 134

def append(model)
  # Wrap results in a promise
  Promise.new.resolve(nil).then do
    send(:<<, model)
  end
end

#attributesObject



77
78
79
# File 'lib/volt/models/array_model.rb', line 77

def attributes
  self
end

#buffer(attrs = {}) ⇒ Object



289
290
291
292
293
294
295
296
297
# File 'lib/volt/models/array_model.rb', line 289

def buffer(attrs = {})
  model_path  = options[:path] + [:[]]
  model_klass = Volt::Model.class_at_path(model_path)

  new_options = options.merge(path: model_path, save_to: self, buffer: true).reject { |k, _| k.to_sym == :persistor }
  model       = model_klass.new(attrs, new_options)

  model
end

#create(model = {}) ⇒ Object

Create does append with a default empty model



142
143
144
# File 'lib/volt/models/array_model.rb', line 142

def create(model={})
  append(model)
end

#delete(val) ⇒ Object



146
147
148
149
150
151
152
153
154
# File 'lib/volt/models/array_model.rb', line 146

def delete(val)
  # Check to make sure the models are allowed to be deleted
  if !val.is_a?(Model) || val.can_delete?
    result = super
    Promise.new.resolve(result)
  else
    Promise.new.reject("permissions did not allow delete for #{val.inspect}.")
  end
end

#fail_not_found_if_nil(promise) ⇒ Object

Raise a RecordNotFoundException if the promise returns a nil.



300
301
302
303
304
305
306
307
308
# File 'lib/volt/models/array_model.rb', line 300

def fail_not_found_if_nil(promise)
  promise.then do |val|
    if val
      val
    else
      raise RecordNotFoundException.new
    end
  end
end

#fetch_first(&block) ⇒ Object

returns a promise to fetch the first instance



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/volt/models/array_model.rb', line 211

def fetch_first(&block)
  persistor = self.persistor

  if persistor && persistor.is_a?(Persistors::ArrayStore)
    # On array store, we wait for the result to be loaded in.
    promise = limit(1).fetch do |res|
      result = res.first

      result
    end
  else
    # On all other persistors, it should be loaded already
    promise = Promise.new.resolve(first)
  end

  # Run any passed in blocks after fetch
  promise = promise.then(&block) if block

  promise
end

#firstObject



156
157
158
159
160
161
162
# File 'lib/volt/models/array_model.rb', line 156

def first
  if persistor.is_a?(Persistors::ArrayStore)
    limit(1)[0]
  else
    self[0]
  end
end

#first!Object

Same as first, except it returns a promise (even on page collection), and it fails with a RecordNotFoundException if no result is found.



166
167
168
# File 'lib/volt/models/array_model.rb', line 166

def first!
  fail_not_found_if_nil(first)
end

#first_or_createObject

Return the first item in the collection, or create one if one does not exist yet.



172
173
174
175
176
177
178
179
180
# File 'lib/volt/models/array_model.rb', line 172

def first_or_create
  first.then do |item|
    if item
      item
    else
      create
    end
  end
end

#inject(*args) ⇒ Object

Make sure it gets wrapped



233
234
235
236
# File 'lib/volt/models/array_model.rb', line 233

def inject(*args)
  args = wrap_values(args)
  super(*args)
end

#inspectObject



275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/volt/models/array_model.rb', line 275

def inspect
  Computation.run_without_tracking do
    # Track on size
    @size_dep.depend
    str = "#<#{self.class}"
    # str += " state:#{loaded_state}"
    # str += " path:#{path.join('.')}" if path
    # str += " persistor:#{persistor.inspect}" if persistor
    str += " #{@array.inspect}>"

    str
  end
end

#lastObject



182
183
184
# File 'lib/volt/models/array_model.rb', line 182

def last
  self[-1]
end

#new_array_model(*args) ⇒ Object



248
249
250
# File 'lib/volt/models/array_model.rb', line 248

def new_array_model(*args)
  ArrayModel.new(*args)
end

#new_model(*args) ⇒ Object



244
245
246
# File 'lib/volt/models/array_model.rb', line 244

def new_model(*args)
  Volt::Model.class_at_path(options[:path]).new(*args)
end

#reverseObject



186
187
188
189
# File 'lib/volt/models/array_model.rb', line 186

def reverse
  @size_dep.depend
  @array.reverse
end

#selectObject

Array#select, with reactive notification



192
193
194
195
196
197
198
199
200
201
202
# File 'lib/volt/models/array_model.rb', line 192

def select
  new_array = []
  @array.size.times do |index|
    value = @array[index]
    if yield(value)
      new_array << value
    end
  end

  new_array
end

#state_for(*args) ⇒ Object



81
82
83
84
85
86
# File 'lib/volt/models/array_model.rb', line 81

def state_for(*args)
  # Track on root dep
  persistor.try(:root_dep).try(:depend)

  super
end

#to_aObject

Convert the model to an array all of the way down



253
254
255
256
257
258
259
260
261
262
# File 'lib/volt/models/array_model.rb', line 253

def to_a
  @size_dep.depend
  array = []
  Volt.run_in_mode(:no_model_promises) do
    attributes.size.times do |index|
      array << deep_unwrap(self[index])
    end
  end
  array
end

#to_jsonObject



264
265
266
267
268
269
270
271
272
# File 'lib/volt/models/array_model.rb', line 264

def to_json
  array = to_a

  if array.is_a?(Promise)
    array.then(&:to_json)
  else
    array.to_json
  end
end