# add methods to Object to determine if this is a dummy object or not class Object def loaded? !loading? end def loading? false end end module ReactiveRecord class Base # A DummyValue stands in for actual value while waiting for load from # server. when value is accessed by most methods it notifies hyper-react # so when the actual value loads react will update. # DummyValue uses the ActiveRecord type info to act like an appropriate # loaded value. class DummyValue < BasicObject def initialize(column_hash = nil) column_hash ||= {} notify @column_hash = column_hash column_type = Base.column_type(@column_hash) || 'nil' default_value_method = "build_default_value_for_#{column_type}" @object = __send__ default_value_method rescue ::Exception end def build_default_value_for_nil @column_hash[:default] || nil end def build_default_value_for_json ::JSON.parse(@column_hash[:default]) if @column_hash[:default] end alias build_default_value_for_jsonb build_default_value_for_json def build_default_value_for_datetime if @column_hash[:default] ::Time.parse(@column_hash[:default].gsub(' ','T')+'+00:00') else ::ReactiveRecord::Base::DummyValue.dummy_time end end alias build_default_value_for_time build_default_value_for_datetime alias build_default_value_for_timestamp build_default_value_for_datetime def build_default_value_for_date if @column_hash[:default] ::Date.parse(@column_hash[:default]) else ::ReactiveRecord::Base::DummyValue.dummy_date end end FALSY_VALUES = [false, nil, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"] def build_default_value_for_boolean !FALSY_VALUES.include?(@column_hash[:default]) end def build_default_value_for_float @column_hash[:default]&.to_f || Float(0.0) end alias build_default_value_for_decimal build_default_value_for_float def build_default_value_for_integer @column_hash[:default]&.to_i || Integer(0) end alias build_default_value_for_bigint build_default_value_for_integer def build_default_value_for_string return @column_hash[:default] if @column_hash[:serialized?] @column_hash[:default] || '' end alias build_default_value_for_text build_default_value_for_string def notify return if ::ReactiveRecord::Base.data_loading? ::ReactiveRecord.loads_pending! ::ReactiveRecord::WhileLoading.loading! end def loading? true end def loaded? false end def nil? true end def ! true end def class notify @object.class end def method_missing(method, *args, &block) if method.start_with?("build_default_value_for_") nil elsif @object || @object.respond_to?(method) notify @object.send method, *args, &block elsif 0.respond_to? method notify 0.send(method, *args, &block) elsif ''.respond_to? method notify ''.send(method, *args, &block) else super end end def coerce(s) # notify # why are we not notifying here return @object.coerce(s) if @object [__send__("to_#{s.class.name.downcase}"), s] end def ==(other) # notify # why are we not notifying here other.object_id == object_id end def object_id `self.$$id` end def is_a?(klass) klass == ::ReactiveRecord::Base::DummyValue end def zero? return @object.zero? if @object false end def to_s notify return @object.to_s if @object '' end def tap yield self self end alias inspect to_s %x{ if (Opal.Object.$$proto) { #{self}.$$proto.toString = Opal.Object.$$proto.toString } else { #{self}.$$prototype.toString = Opal.Object.$$prototype.toString } } def to_f notify return @object.to_f if @object 0.0 end def to_i notify return @object.to_i if @object 0 end def to_numeric notify return @object.to_numeric if @object 0 end def to_number notify return @object.to_number if @object 0 end def self.dummy_time @dummy_time ||= ::Time.parse('2001-01-01T00:00:00.000-00:00') end def self.dummy_date @dummy_date ||= ::Date.parse('1/1/2001') end def to_date notify return @object.to_date if @object ::ReactiveRecord::Base::DummyValue.dummy_date end def to_time notify return @object.to_time if @object ::ReactiveRecord::Base::DummyValue.dummy_time end def acts_as_string? return true if @object.is_a? ::String return @object.acts_as_string? if @object && @object.respond_to?(:acts_as_string?) true end # this is a hackish way and compatible with any other rendered object # to identify a DummyValue during render # in ReactRenderingContext.run_child_block() and # to convert it to a string, for rendering # advantage over a try(:method) is, that it doesnt raise und thus is faster # which is important during render def respond_to?(method, all = false) return true if method == :acts_as_string? return true if %i[inspect to_date to_f to_i to_numeric to_number to_s to_time].include? method return @object.respond_to?(method, all) if @object false end def try(*args, &b) if args.empty? && block_given? yield self else __send__(*args, &b) end rescue ::Exception nil end end end end