class Volt::Model
Constants
- INVALID_FIELD_NAMES
Attributes
attributes[R]
options[R]
parent[R]
path[R]
persistor[R]
Public Class Methods
new(attributes = {}, options = {}, initial_state = nil)
click to toggle source
# File lib/volt/models/model.rb, line 64 def initialize(attributes = {}, options = {}, initial_state = nil) # Start off with empty attributes @attributes = {} # The listener event counter keeps track of how many computations are listening on this model @listener_event_counter = EventCounter.new( -> { parent.try(:persistor).try(:listener_added) }, -> { parent.try(:persistor).try(:listener_removed) } ) # The root dependency is used to track if anything is using the data from this # model. That information is relayed to the ArrayModel so it knows when it can # stop subscribing. # @root_dep = Dependency.new(@listener_event_counter.method(:add), @listener_event_counter.method(:remove)) @root_dep = Dependency.new(-> { retain }, -> { release }) @deps = HashDependency.new @size_dep = Dependency.new self.options = options @new = (initial_state != :loaded) assign_attributes(attributes, true) # The persistor is usually responsible for setting up the loaded_state, if # there is no persistor, we set it to loaded if @persistor @persistor.loaded(initial_state) else change_state_to(:loaded_state, initial_state || :loaded, false) end # Trigger the new event, pass in :new trigger!(:new, :new) end
Private Class Methods
inherited(subclass)
click to toggle source
# File lib/volt/models/model.rb, line 433 def self.inherited(subclass) if defined?(RootModels) RootModels.add_model_class(subclass) end end
process_class_name(name)
click to toggle source
# File lib/volt/models/model.rb, line 439 def self.process_class_name(name) name.singularize end
Public Instance Methods
!()
click to toggle source
Pass through needed
# File lib/volt/models/model.rb, line 178 def ! !attributes end
==(val)
click to toggle source
Pass the comparison through
Calls superclass method
# File lib/volt/models/model.rb, line 167 def ==(val) if val.is_a?(Model) # Use normal comparison for a model super else # Compare to attributes otherwise attributes == val end end
_id()
click to toggle source
# File lib/volt/models/model.rb, line 121 def _id get(:id) end
assign_attributes(attrs, initial_setup = false, skip_changes = false)
click to toggle source
Assign multiple attributes as a hash, directly.
# File lib/volt/models/model.rb, line 140 def assign_attributes(attrs, initial_setup = false, skip_changes = false) attrs = wrap_values(attrs) if attrs # When doing a mass-assign, we don't validate or save until the end. if initial_setup || skip_changes Model.no_change_tracking do assign_all_attributes(attrs, skip_changes) end else assign_all_attributes(attrs) end else # Assign to empty @attributes = {} end # Trigger and change all @deps.changed_all! @deps = HashDependency.new run_initial_setup(initial_setup) end
Also aliased as: attributes=
destroy()
click to toggle source
# File lib/volt/models/model.rb, line 335 def destroy if parent result = parent.delete(self) # Wrap result in a promise if it isn't one return result#.then else fail 'Model does not have a parent and cannot be deleted.' end end
get(attr_name, expand = false)
click to toggle source
When reading an attribute, we need to handle reading on: 1) a nil model, which returns a wrapped error 2) reading directly from attributes 3) trying to read a key that doesn't exist.
# File lib/volt/models/model.rb, line 239 def get(attr_name, expand = false) # Reading an attribute, we may get back a nil model. attr_name = attr_name.to_sym check_valid_field_name(attr_name) # Track that something is listening @root_dep.depend # Track dependency @deps.depend(attr_name) # See if the value is in attributes if @attributes && @attributes.key?(attr_name) return @attributes[attr_name] else # If we're expanding, or the get is for a collection, in which # case we always expand. plural_attr = attr_name.plural? if expand || plural_attr new_value = read_new_model(attr_name) # A value was generated, store it if new_value # Assign directly. Since this is the first time # we're loading, we can just assign. # # Don't track changes if we're setting a collection Volt.run_in_mode_if(plural_attr, :no_change_tracking) do set(attr_name, new_value) end end return new_value else return nil end end end
id()
click to toggle source
# File lib/volt/models/model.rb, line 113 def id get(:id) end
id=(val)
click to toggle source
# File lib/volt/models/model.rb, line 117 def id=(val) set(:id, val) end
inspect()
click to toggle source
# File lib/volt/models/model.rb, line 312 def inspect Computation.run_without_tracking do str = "#<#{self.class}" # str += ":#{object_id}" # First, select all of the non-ArrayModel values attrs = attributes.reject {|key, val| val.is_a?(ArrayModel) }.to_h # Show the :id first, then sort the rest of the attributes id = attrs.delete(:id) id = id[0..3] + '..' + id[-4..-1] if id attrs = attrs.sort attrs.insert(0, [:id, id]) if id str += attrs.map do |key, value| " #{key}: #{value.inspect}" end.join(',') str += '>' str end end
method_missing(method_name, *args, &block)
click to toggle source
Calls superclass method
Volt::Models::Helpers::Dirty#method_missing
# File lib/volt/models/model.rb, line 182 def method_missing(method_name, *args, &block) if method_name[0] == '_' # Remove underscore method_name = method_name[1..-1] if method_name[-1] == '=' # Assigning an attribute without the = set(method_name[0..-2], args[0], &block) else # If the method has an ! on the end, then we assign an empty # collection if no result exists already. expand = (method_name[-1] == '!') method_name = method_name[0..-2] if expand get(method_name, expand) end else # Call on parent super end end
new?()
click to toggle source
Return true if the model hasn't been saved yet
# File lib/volt/models/model.rb, line 126 def new? @new end
new_array_model(attributes, options)
click to toggle source
# File lib/volt/models/model.rb, line 304 def new_array_model(attributes, options) # Start with an empty query options = options.dup options[:query] = [] Volt::ArrayModel.class_at_path(options[:path]).new(attributes, options) end
new_model(attributes = {}, new_options = {}, initial_state = nil)
click to toggle source
# File lib/volt/models/model.rb, line 298 def new_model(attributes = {}, new_options = {}, initial_state = nil) new_options = new_options.merge(persistor: @persistor) Volt::Model.class_at_path(new_options[:path]).new(attributes, new_options, initial_state) end
options=(options)
click to toggle source
Update the options
# File lib/volt/models/model.rb, line 131 def options=(options) @options = options @parent = options[:parent] @path = options[:path] || [] @class_paths = options[:class_paths] @persistor = setup_persistor(options[:persistor]) end
read_new_model(method_name)
click to toggle source
Get a new model, make it easy to override
# File lib/volt/models/model.rb, line 284 def read_new_model(method_name) if @persistor && @persistor.respond_to?(:read_new_model) return @persistor.read_new_model(method_name) else opts = @options.merge(parent: self, path: path + [method_name]) if method_name.plural? return new_array_model([], opts) else return new_model({}, opts) end end end
release()
click to toggle source
# File lib/volt/models/model.rb, line 104 def release @listener_event_counter.remove end
respond_to_missing?(method_name, include_private = false)
click to toggle source
Calls superclass method
# File lib/volt/models/model.rb, line 279 def respond_to_missing?(method_name, include_private = false) method_name.to_s.start_with?('_') || super end
retain()
click to toggle source
# File lib/volt/models/model.rb, line 100 def retain @listener_event_counter.add end
set(attribute_name, value, &block)
click to toggle source
Do the assignment to a model and trigger a changed event
# File lib/volt/models/model.rb, line 204 def set(attribute_name, value, &block) # Assign, without the = attribute_name = attribute_name.to_sym check_valid_field_name(attribute_name) old_value = @attributes[attribute_name] new_value = wrap_value(value, [attribute_name]) if old_value != new_value # Track the old value, skip if we are in no_validate attribute_will_change!(attribute_name, old_value) unless Volt.in_mode?(:no_change_tracking) # Assign the new value @attributes[attribute_name] = new_value @deps.changed!(attribute_name) @size_dep.changed! if old_value.nil? || new_value.nil? # TODO: Can we make this so it doesn't need to be handled for non store collections # (maybe move it to persistor, though thats weird since buffers don't have a persistor) clear_server_errors(attribute_name) if @server_errors.present? # Save the changes run_changed(attribute_name) unless Volt.in_mode?(:no_change_tracking) end new_value end
state_for(*args)
click to toggle source
Calls superclass method
Volt::StateManager#state_for
# File lib/volt/models/model.rb, line 108 def state_for(*args) @root_dep.depend super end
to_json()
click to toggle source
# File lib/volt/models/model.rb, line 353 def to_json to_h.to_json end
update(attrs)
click to toggle source
Update tries to update the model and returns
# File lib/volt/models/model.rb, line 358 def update(attrs) old_attrs = @attributes.dup Model.no_change_tracking do assign_all_attributes(attrs, false) validate!.then do |errs| if errs && errs.present? # Revert wholesale @attributes = old_attrs Promise.new.resolve(errs) else # Persist persist_changes(nil) end end end end
Private Instance Methods
assign_all_attributes(attrs, track_changes = false)
click to toggle source
Used internally from other methods that assign all attributes
# File lib/volt/models/model.rb, line 409 def assign_all_attributes(attrs, track_changes = false) # Assign each attribute using setters attrs.each_pair do |key, value| key = key.to_sym # Track the change, since assign_all_attributes runs with no_change_tracking old_val = @attributes[key] attribute_will_change!(key, old_val) if track_changes && old_val != value if self.respond_to?(:"#{key}=") # If a method without an underscore is defined, call that. send(:"#{key}=", value) else # Otherwise, use the _ version set(key, value) end end # Make an id if there isn't one yet if @attributes[:id].nil? && persistor.try(:auto_generate_id) @attributes[:id] = generate_id end end
check_valid_field_name(name)
click to toggle source
Volt
provides a few access methods to get more data about the model, we want to prevent these from being assigned or accessed through underscore methods.
# File lib/volt/models/model.rb, line 402 def check_valid_field_name(name) if INVALID_FIELD_NAMES[name] fail InvalidFieldName, "`#{name}` is reserved and can not be used as a field" end end
run_initial_setup(initial_setup)
click to toggle source
# File lib/volt/models/model.rb, line 377 def run_initial_setup(initial_setup) # Save the changes if initial_setup # Run initial validation if Volt.in_mode?(:no_validate) # No validate, resolve self Promise.new.resolve(self) else return validate!.then do |errs| if errs && errs.size > 0 Promise.new.reject(errs) else Promise.new.resolve(self) end end end else return run_changed end end