lib/rsence/value.rb in rsence-pre-3.0.0.8 vs lib/rsence/value.rb in rsence-pre-3.0.0.9

- old
+ new

@@ -3,102 +3,106 @@ # HValue is the model for client-server synchronized values. # A value contains its payload {#data} and enough meta-data to define its behavior. class HValue - + # The validity of the data. Defaults to false, when the data comes from the client. # @return [Boolean] True, when set by the server. False when initially set by the client. Also false, unless all responders return true. attr_reader :is_valid - + # @private Is true when changed by the server. Causes the ValueManager to send its client-side representation. attr_reader :sync - + # @private The unique ID of the value. attr_reader :value_id alias val_id value_id - + # The payload data. Use {#set} to change. attr_reader :data - + # @private List of responders attr_reader :members - + # @private attr_writer :is_valid - + # @private def value_id=(new_id) @value_id = new_id end # @private alias val_id= value_id= - + alias valid is_valid alias valid? is_valid - + # @private Method for binding the value to the session data. def add( msg ) - + # get the value storage from the session data session_values = msg.session[:values][:by_id] - + ## Store the object here session_values[ @value_id ] = self - + ## Sends the client-side description restore( msg ) - + ## Set the valid flag, so we know that the value is initially in sync @is_valid = true end - + # @private (Re-)Send the client-size representation def restore( msg ) - + ## Tags itself as a new value from the client's point of view @is_new_to_client = true - + add_to_sync( msg ) - + end - + # Value meta-data. The third constructor parameter attr_accessor :meta - + # Creates a new client-server automatically synchronized data wrapper object. # @param [Message] msg Just pass on the +msg+ from the scope you call from. # @param [#to_json] data Any data that can be converted to JSON. # @param [Hash] meta Has no effect yet. - def initialize( msg, data, meta = { :name => nil } ) - + def initialize( msg, data, meta = { :name => nil, :type => 0 } ) + ## Get an unique integer id for the value if RSence.args[:debug] and meta[:name] and not msg.valuemanager.id_exists?( msg, meta[:name] ) @value_id = meta[:name] else @value_id = msg.valuemanager.ses_unique_id( msg ) end - + @meta = meta - + + if meta[:type] == 2 + @buffer = [] + end + ## set the data of the hvalue set( msg, data, true ) - + ## the @sync flag is raised, when the client data is older than the server data @sync = false - + ## the @is_valid flag is lowered, when the client data is newer than the server data @is_valid = true - + ## Bind the value to the value manager and report it to the client add( msg ) - + ## storage for validator bindings @members = {} - + end - + # Binds the value to a responder. The responder is an instance of {Plugins::Plugin__ Plugin} or {Plugins::GUIPlugin__ GUIPlugin}. # Responders are called once, when the client requests the data to be changed. # Responders must return +true+, if they accept the change. Otherwise the data is treated as invalid. # Responders must respond to exactly two parameters: ( (Message) +msg+, (HValue) +value+ ) # @@ -110,19 +114,19 @@ method_name = method_name.to_sym unless method_name.class == Symbol @members[plugin_name] = [] unless @members.has_key?( plugin_name ) @members[plugin_name].push( method_name ) unless @members[plugin_name].include?( method_name ) return true end - + # Checks, if the plugin_name and method_name pairing is already bound with the bind method. Returns true or false. def bound?( plugin_name, method_name ) plugin_name = plugin_name.to_sym unless plugin_name.class == Symbol method_name = method_name.to_sym unless method_name.class == Symbol return false unless @members.has_key?(plugin_name) return @members[plugin_name].include?(method_name) end - + # Releases the responder of the value, both params as in bind, but optional +method_name+ can be omitted, matching all methods bound to the +plugin_name+. # @param [Symbol] plugin_name The name of the plugin acting as a responder to the value. # @param [Symbol] method_name The name of the method of the plugin acting as a responder to the value. # @return [Boolean] Returns true, if successful, false if not bound or other error. def release( plugin_name=false, method_name=false ) @@ -130,144 +134,160 @@ method_name = method_name.to_sym if method_name.class == String return release_all if not plugin_name and not method_name return false unless @members.has_key?( plugin_name ) if not method_name @members.delete( plugin_name ) - else + else @members[plugin_name].slice!(@members[plugin_name].index( method_name )) if @members[plugin_name].include?(method_name) if @members[plugin_name].empty? @members.delete( plugin_name ) end end return true end - + # Releases all responders. # @return [true] def release_all @members = {} return true end - + # @deprecated Use {#release} as the opposite to bind. alias unbind release - + # @private Tell all bound instances that the value is changed. def tell( msg ) invalid_count = 0 @members.each_key do |plugin_name| @members[plugin_name].each do |method_name| - invalid_count += 1 unless msg.plugins.run_plugin( plugin_name, method_name, msg, self ) + if @meta[:type] == 1 + data = @data + for item in data + @data = item + msg.plugins.run_plugin( plugin_name, method_name, msg, self ) + end + @data = data + else + invalid_count += 1 unless msg.plugins.run_plugin( plugin_name, method_name, msg, self ) + end end end if invalid_count == 0 @is_valid = true msg.session[:values][:check].delete( @value_id ) end end - + # @private Handle updates from the client. def from_client( msg, data ) # only process changes, if different from the one already stored. if @data != data - + # puts "data sync from client: #{@data.inspect} -> #{data.inspect} (#{@meta[:name]})" ## set takes care of the setting.. @data = data - + ## change the valid state, because the value was set by the client! @is_valid = false - + ## add the id to the values to be checked check_ids = msg.session[:values][:check] unless check_ids.include?( @value_id ) check_ids.push( @value_id ) end end - + end - + # @private Adds the value to the sync array. def add_to_sync( msg ) ## add the id to the values to be synchronized (to client) sync_ids = msg.session[:values][:sync] unless sync_ids.include?( @value_id ) sync_ids.push( @value_id ) end end - + # Sets the data of the value, the change will be synced with the client. # @param [Message] msg The {Message} instance. # @param [#to_json] data Any data that can be mapped to JSON and handled by the client. # @param [Boolean] dont_tell_client Doesn't notify the client about the change, if true. def set( msg, data, dont_tell_client=false ) - + @data = data - + # won't tell the client about the change, usually not needed unless dont_tell_client + @buffer.push( data ) if @meta[:type] == 2 ## update the flags @sync = false @is_valid = true - + add_to_sync( msg ) end end - + # Sets the key of the hash data of the value, the change will be synced with the client. # @param [Message] msg The {Message} instance. # @param [String] key The key of data to change # @param [#to_json] data Any data that can be mapped to JSON and handled by the client. # @param [Boolean] dont_tell_client Doesn't notify the client about the change, if true. def set_key( msg, key, data, dont_tell_client=false ) - + @data[key] = data - + # won't tell the client about the change, usually not needed unless dont_tell_client ## update the flags @sync = false @is_valid = true - + add_to_sync( msg ) end end - + # @private Tell the client that the value changed. def to_client( msg ) + if @meta[:type] == 2 + data = @buffer.clone + @buffer = [] + else + data = @data + end if @is_new_to_client ## Initialize a new client value - msg.reply_value( :new, @value_id, @data ) + msg.reply_value( :new, @value_id, data, @meta[:type] ) @is_new_to_client = false else ## Sets the client value - msg.reply_value( :set, @value_id, @data ) + msg.reply_value( :set, @value_id, data ) end end - + # Destructor method. If msg is supplied, deletes the client representation too. # @param [false, Message] A {Message} instance. When supplied, deletes the client representation. def die!( msg=false ) - + release_all - + # get the value storage from the session data session_values = msg.session[:values][:by_id] - + ## Store the object here session_values.delete( @value_id ) - + if msg and not @is_new_to_client msg.reply_value( :del, @value_id ) end end alias die die! def inspect "#<RSence::HValue value_id:#{@value_id.inspect}, valid: #{@is_valid.inspect}, sync: #{@sync.inspect}, is_new_to_client: #{@is_new_to_client.inspect}, meta: #{@meta.inspect[0..100]}, data: #{@data.inspect[0..100]} ...>" end - + end end