diff options
-rw-r--r-- | lib/edoors/particle.rb | 241 |
1 files changed, 189 insertions, 52 deletions
diff --git a/lib/edoors/particle.rb b/lib/edoors/particle.rb index 84f6ece..983af01 100644 --- a/lib/edoors/particle.rb +++ b/lib/edoors/particle.rb @@ -24,6 +24,33 @@ module Edoors # class Particle # + # creates a Particle object from the arguments. + # + # @param [Hash] o a customizable set of options + # + # @option o 'ts' [String] + # Creation time + # @option o 'src' [String] + # Iota where it's originated from + # @option o 'dst' [String] + # Iota where it's heading to + # @option o 'room' [String] + # Room path part of the current destination + # @option o 'door' [String] + # Door path part of the current destination + # @option o 'action' [String] + # action part of the current destination + # @option o 'dsts' [String] + # fifo of path?action strings where to travel to + # @option o 'link_keys' [String] + # unordered keys used has payload keys to build link_value + # @option o 'payload' [String] + # the data carried by this particle + # @option o 'merged' [String] + # list of merged particles + # + # @see Spin#require_p require a Particle + # def initialize o={} @ts = Time.now # creation time @src = nil # Iota where it's originated from @@ -31,11 +58,10 @@ module Edoors @room = nil # Room path part of the current destination @door = nil # Door path part of the current destination @action = nil # action part of the current destination - @link_value = nil # the value computed with the link_fields values extracted from the payload - # used for pearing Particles in Boards and linking in routing process @dsts = [] # fifo of path?action strings where to travel to - @link_fields = [] # the fields used to generate the link value - @payload = {} # the actual data carried by this particle + @link_keys = [] # unordered keys used has payload keys to build link_value + @link_value = {} # the payload keys and values corresponding to the link keys + @payload = {} # the data carried by this particle @merged = [] # list of merged particles # if not o.empty? @@ -46,77 +72,113 @@ module Edoors @payload = o['payload']||{} @src = o['spin'].search_down o['src'] if o['src'] @dst = o['spin'].search_down o['dst'] if o['dst'] - o['dsts'].each do |dst| add_dsts dst end if o['dsts'] - set_link_fields *o['link_fields'] if o['link_fields'] + add_dsts *o['dsts'] if o['dsts'] + set_link_keys *o['link_keys'] if o['link_keys'] o['merged'].each do |particle| merge! Particle.json_create(particle.merge!('spin'=>o['spin'])) end if o['merged'] end end # + # called by JSON#generate to serialize the Particle object into JSON data + # + # @param [Array] a belongs to JSON generator + # def to_json *a { - 'kls' => self.class.name, - 'ts' => @ts, - 'src' => (@src ? @src.path : nil ), - 'dst' => (@dst ? @dst.path : nil ), - 'room' => @room, - 'door' => @door, - 'action' => @action, - 'dsts' => @dsts, - 'link_fields' => @link_fields, - 'payload' => @payload, - 'merged' => @merged + 'kls' => self.class.name, + 'ts' => @ts, + 'src' => (@src ? @src.path : nil ), + 'dst' => (@dst ? @dst.path : nil ), + 'room' => @room, + 'door' => @door, + 'action' => @action, + 'dsts' => @dsts, + 'link_keys' => @link_keys, + 'payload' => @payload, + 'merged' => @merged }.to_json *a end # + # creates a Particle object from a JSON data + # + # @param [Hash] o belongs to JSON parser + # def self.json_create o raise Edoors::Exception.new "JSON #{o['kls']} != #{self.name}" if o['kls'] != self.name self.new o end # - # called when released + # clears all attributes + # + # @see Spin#release_p called whe na Particle is released + # def reset! clear_merged! ( @src ? @src : ( @dst ? @dst : nil ) ) - @ts = @src = @dst = @room = @door = @action = @link_value = nil + @ts = @src = @dst = @room = @door = @action = nil @dsts.clear - @link_fields.clear + @link_value.clear + @link_keys.clear @payload.clear end # - # called when sent + # sets @src, @ts, and reset others + # + # @see Particle#apply_link! called when a Link is applied + # @see Door#_send called when a Door sends a Particle + # def init! src @src = src @ts = Time.now @dst = @room = @door = @action = nil end # - attr_reader :ts, :src, :dst, :room, :door, :action, :link_value, :payload + attr_reader :ts, :src, :dst, :room, :door, :action, :payload, :link_value # - # routing + # returns the next destination # def next_dst @dsts[0] end # + # clears the destination list + # def clear_dsts! @dsts.clear end # - def add_dsts dsts - dsts.split(Edoors::LINK_SEP).each do |dst| - if dst.empty? or dst[0]==Edoors::PATH_SEP or dst[0]==Edoors::PATH_SEP or dst=~/\/\?/\ - or dst=~/\/{2,}/ or dst=~/\s+/ or dst==Edoors::ACT_SEP + # adds destinations to the destination list + # + # @param [Array] dsts destinations to add + # + # The parameters are checked before beeing added. + # they must not be empty or be '?' or start with '/' + # or contain '/?' or '//' or '\s+' + # + def add_dsts *dsts + dsts.each do |dst| + if dst.empty? or dst==Edoors::ACT_SEP or dst[0]==Edoors::PATH_SEP \ + or dst=~/\/\?/ or dst=~/\/{2,}/ or dst=~/\s+/ raise Edoors::Exception.new "destination #{dst} is not acceptable" end @dsts << dst end end # + # adds a destination to the destination list + # + # @param [String] a the action + # @param [String] d the destination + # def add_dst a, d='' add_dsts d+Edoors::ACT_SEP+a end # + # sets the current destination + # + # @param [String] a the action + # @param [String Iota] d the destination + # def set_dst! a, d @action = a if d.is_a? Edoors::Iota @@ -126,6 +188,11 @@ module Edoors end end # + # splits the next destination into @room, @door, @action attributes + # + # the @dst attribute is set to nil + # the @room, @door, @action attributes are set to nil if not defined + # def split_dst! @dst = @room = @door = @action = nil return if (n = next_dst).nil? @@ -133,6 +200,10 @@ module Edoors _split_path! p end # + # called by Particle#split_dst! to split the path part of the destination + # + # @param [String] p path to be splitted + # def _split_path! p i = p.rindex Edoors::PATH_SEP if i.nil? @@ -146,81 +217,147 @@ module Edoors end private :_split_path! # + # sets the current destination and shift the head of destination list + # + # @param [Iota] dst the current destination + # + # @see Room#_route routing success + # @see Room#_send routing failure + # def dst_routed! dst @dst = dst @dsts.shift end # + # sets the error message, the destination and action + # + # @param [String] e error message + # @param [Iota] dst the destination, @src if nil + # + # the error message is set into @payload[[Edoors::FIELD_ERROR_MSG] + # def error! e, dst=nil @action = Edoors::ACT_ERROR @dst = dst||@src @payload[Edoors::FIELD_ERROR_MSG]=e end # + # applies the effects of the given Link + # + # @param [Link] lnk the link to apply effects + # + # updates @src with Link @src, clears the destination list + # adds the Link destinations to @dsts, sets the @link_keys + # def apply_link! lnk init! lnk.door clear_dsts! - add_dsts lnk.dsts - set_link_fields lnk.fields + add_dsts *lnk.dsts + set_link_keys *lnk.keys end # - # data manipulation + # adds/updates a key value pair into payload # - def []= k, v - @payload[k]=v - compute_link_value! if @link_fields.include? k - end + # @param [String] k the key + # @param [Object] v the value + # + # \@link_value attribute will be updated if impacted # - def set_data k, v + def []= k, v + @link_value[k] = v if @link_keys.include? k @payload[k] = v - compute_link_value! if @link_fields.include? k end + alias :set_data :[]= # - def [] k - @payload[k] + # destroys the value paired with a key + # + # @param [String] k the key + # + # @return the associated value + # + # \@link_value attribute will be updated if impacted + # + def del_data k + @link_value.delete k if @link_keys.include? k + @payload.delete k end # - def get_data k + # retrieves a data value from a key + # + # @param [String] k the key + # + def [] k @payload[k] end - alias :data :get_data + # + alias :get_data :[] + alias :data :[] + # + # clones the payload of the given Particle + # + # @param [Particle] p the Particle to clone the payload of # def clone_data p @payload = p.payload.clone end # - # link value and fields + # sets the links keys + # + # @param [Array] args list of keys to set # - def set_link_fields *args - @link_fields.clear if not @link_fields.empty? + # \@link_value attribute will be updated + # + def set_link_keys *args + @link_keys.clear if not @link_keys.empty? args.compact! - args.each do |lfs| - lfs.split(',').each do |lf| - @link_fields << lf - end + args.each do |lf| + @link_keys << lf end - compute_link_value! + @link_value = @payload.select { |k,v| @link_keys.include? k } end # - def compute_link_value! - @link_value = @link_fields.inject('') { |s,lf| s+=@payload[lf].to_s if @payload[lf]; s } + # tries to link the Particle with the given Link + # + # @param [Link] link the link to try to link with + # + # returns true if the value of the Link is nil + # otherwise checks if the extracted key values pairs from the Particle + # payload using the Link value keys as selectors, equals the Link value + # + # @return [Boolean] true if the Link links with the Particle + # + def link_with? link + return true if link.value.nil? + link.value.keys.inject({}) { |h,k| h[k]=@payload[k] if @payload.has_key?(k); h }.eql? link.value end # - # merge particles management + # merges the given Particle in + # + # @param [Particle] p the Particle to merge in # def merge! p @merged << p end # + # returns a merged Particle + # + # @param [Integer] i the index into the merged Particle list + # def merged i @merged[i] end # + # shifts the merged Particle list + # def merged_shift @merged.shift end # - def clear_merged! r=nil + # recursively clears the merged Particle list + # + # @param [Boolean] r releases the cleared Particle if true + # + def clear_merged! r=false @merged.each do |p| p.clear_merged! r r.release_p p if r |