class Volt::ArrayModel

Attributes

array[R]
options[R]
parent[R]
path[R]
persistor[R]

Public Class Methods

new(array = [], options = {}) click to toggle source
Calls superclass method
# File lib/volt/models/array_model.rb, line 89
def initialize(array = [], options = {})
  @options   = options
  @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
process_class_name(name) click to toggle source
# File lib/volt/models/array_model.rb, line 306
def self.process_class_name(name)
  name.pluralize
end
proxy_to_persistor(*method_names) click to toggle source

Some methods get passed down to the persistor.

# File lib/volt/models/array_model.rb, line 75
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) click to toggle source

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.

Calls superclass method
# File lib/volt/models/array_model.rb, line 24
def self.proxy_with_load(*method_names)
  imethods = instance_methods(false)
  method_names.each do |method_name|
    # Sometimes we want to alias a method_missing method, so we use super
    # instead to call it, if its not defined locally.
    if imethods.include?(method_name)
      imethod = true
      old_method_name = :"__old_#{method_name}"
      alias_method(old_method_name, method_name)
    else
      imethod = false
    end

    define_method(method_name) do |*args, &block|
      if imethod
        call_orig = proc do |*args|
          send(old_method_name, *args)
        end
      else
        call_orig = proc do |*args|
          super(*args)
        end
      end

      # track on the root dep
      persistor.try(:root_dep).try(:depend)

      if persistor.respond_to?(:run_once_loaded) &&
          !Volt.in_mode?(:no_model_promises)
        promise = persistor.run_once_loaded.then do
          # We are already loaded and the result is going to be wrapped
          Volt.run_in_mode(:no_model_promises) do
            call_orig.call(*args)
          end
        end

        if block
          promise = promise.then do |val|
            block.call(val)
          end
        end

        promise
      else
        call_orig.call(*args)
      end
    end
  end
end

Public Instance Methods

+(*args) click to toggle source

Make sure it gets wrapped

Calls superclass method
# File lib/volt/models/array_model.rb, line 232
def +(*args)
  args = wrap_values(args)
  super(*args)
end
<<(model) click to toggle source

Make sure it gets wrapped

# File lib/volt/models/array_model.rb, line 132
def <<(model)
  create_new_model(model, :<<)
end
all() click to toggle source

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

# File lib/volt/models/array_model.rb, line 215
def all
  self
end
append(model) click to toggle source

Works like << except it always returns a promise

# File lib/volt/models/array_model.rb, line 139
def append(model)
  create_new_model(model, :append)
end
Also aliased as: reactive_array_append
attributes() click to toggle source
# File lib/volt/models/array_model.rb, line 120
def attributes
  self
end
buffer(attrs = {}) click to toggle source
# File lib/volt/models/array_model.rb, line 285
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
count(&block) click to toggle source
# File lib/volt/models/array_model.rb, line 311
def count(&block)
  all.reactive_count(&block)
end
Also aliased as: reactive_count
create(model={}) click to toggle source

Create does append with a default empty model

# File lib/volt/models/array_model.rb, line 144
def create(model={})
  create_new_model(model, :create)
end
delete(val) click to toggle source
Calls superclass method
# File lib/volt/models/array_model.rb, line 149
def delete(val)
  # Check to make sure the models are allowed to be deleted
  if !val.is_a?(Model)
    # Not a model, return as a Promise
    super(val).then
  else
    val.can_delete?.then do |can_delete|
      if can_delete
        super(val)
      else
       Promise.new.reject("permissions did not allow delete for #{val.inspect}.")
      end
    end
  end
end
fail_not_found_if_nil(promise) click to toggle source

Raise a RecordNotFoundException if the promise returns a nil.

# File lib/volt/models/array_model.rb, line 296
def fail_not_found_if_nil(promise)
  promise.then do |val|
    if val
      val
    else
      raise RecordNotFoundException.new
    end
  end
end
fetch_first(&block) click to toggle source

returns a promise to fetch the first instance

# File lib/volt/models/array_model.rb, line 220
def fetch_first(&block)
  Volt.logger.warn('.fetch_first is deprecated in favor of .first')
  first
end
first() click to toggle source
# File lib/volt/models/array_model.rb, line 165
def first
  if persistor.is_a?(Persistors::ArrayStore)
    limit(1)[0]
  else
    self[0]
  end
end
first!() click to toggle source

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

# File lib/volt/models/array_model.rb, line 175
def first!
  fail_not_found_if_nil(first)
end
first_or_create() click to toggle source

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

# File lib/volt/models/array_model.rb, line 181
def first_or_create
  first.then do |item|
    if item
      item
    else
      create
    end
  end
end
flatten(*args) click to toggle source
# File lib/volt/models/array_model.rb, line 237
def flatten(*args)
  wrap_values(to_a.flatten(*args))
end
inject(*args) click to toggle source

Make sure it gets wrapped

Calls superclass method
# File lib/volt/models/array_model.rb, line 226
def inject(*args)
  args = wrap_values(args)
  super(*args)
end
inspect() click to toggle source
# File lib/volt/models/array_model.rb, line 273
def inspect
  # 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
last() click to toggle source
# File lib/volt/models/array_model.rb, line 191
def last
  self[-1]
end
new(*args)
Alias for: new_model
new_array_model(*args) click to toggle source
# File lib/volt/models/array_model.rb, line 246
def new_array_model(*args)
  Volt::ArrayModel.class_at_path(options[:path]).new(*args)
end
new_model(*args) click to toggle source
# File lib/volt/models/array_model.rb, line 241
def new_model(*args)
  Volt::Model.class_at_path(options[:path]).new(*args)
end
Also aliased as: new
parent=(val) click to toggle source
# File lib/volt/models/array_model.rb, line 104
def parent=(val)
  @options[:parent] = val
end
path=(val) click to toggle source
# File lib/volt/models/array_model.rb, line 116
def path=(val)
  @options[:path] = val
end
reactive_array_append(model)

Alias append for use inside of child append

Alias for: append
reactive_count(&block)
Alias for: count
reverse() click to toggle source
# File lib/volt/models/array_model.rb, line 195
def reverse
  @size_dep.depend
  @array.reverse
end
select() { |value| ... } click to toggle source

Array#select, with reactive notification

# File lib/volt/models/array_model.rb, line 201
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) click to toggle source
Calls superclass method Volt::StateManager#state_for
# File lib/volt/models/array_model.rb, line 124
def state_for(*args)
  # Track on root dep
  persistor.try(:root_dep).try(:depend)

  super
end
to_a() click to toggle source

Convert the model to an array all of the way down

# File lib/volt/models/array_model.rb, line 251
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_json() click to toggle source
# File lib/volt/models/array_model.rb, line 262
def to_json
  array = to_a

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

Private Instance Methods

create_new_model(model, from_method) click to toggle source

called form <<, append, and create. If a hash is passed in, it converts it to a model. Then it takes the model and inserts it into the ArrayModel then persists it.

# File lib/volt/models/array_model.rb, line 319
def create_new_model(model, from_method)
  if model.is_a?(Model)
    if model.buffer?
      fail "The #{from_method} does not take a buffer.  Call .save! on buffer's to persist them."
    end

    # Set the new path and the persistor.
    model.options = @options.merge(parent: self, path: @options[:path] + [:[]])
  else
    model = wrap_values([model]).first
  end


  if model.is_a?(Model)
    promise = model.can_create?.then do |can_create|
      unless can_create
        fail "permissions did not allow create for #{model.inspect}"
      end
    end.then do

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

      @persistor.added(model, @array.size - 1)
    end.then do
      nil.then do
        # Validate and save
        model.run_changed
      end.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)

      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)

        # remove from the id list
        @persistor.try(:remove_tracking_id, model)

        # re-raise, err might not be an Error object, so we use a rejected promise to re-raise
        Promise.new.reject(err)
      end
    end
  else
    promise = nil.then do
      # Add it to the array and trigger any watches or on events.
      reactive_array_append(model)

      @persistor.added(model, @array.size - 1)
    end
  end

  promise = promise.then do
    # resolve the model
    model
  end

  # unwrap the promise if the persistor is synchronus.
  # This will return the value or raise the exception.
  promise = promise.unwrap unless @persistor.async?

  # return
  promise
end