slow_blink 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/slow_blink/ext_schema_parser/lexer.c +168 -177
- data/ext/slow_blink/ext_schema_parser/lexer.h +1 -1
- data/ext/slow_blink/ext_schema_parser/parser.c +303 -306
- data/ext/slow_blink/ext_schema_parser/parser.h +11 -12
- data/ext/slow_blink/{ext_compact_encoder → message/ext_compact_encoder}/ext_compact_encoder.c +3 -1
- data/ext/slow_blink/message/ext_compact_encoder/extconf.rb +4 -0
- data/lib/slow_blink/error.rb +1 -1
- data/lib/slow_blink/message/binary.rb +19 -26
- data/lib/slow_blink/message/boolean.rb +17 -17
- data/lib/slow_blink/message/date.rb +24 -0
- data/lib/slow_blink/message/decimal.rb +34 -0
- data/lib/slow_blink/message/enumeration.rb +24 -23
- data/lib/slow_blink/message/field.rb +45 -6
- data/lib/slow_blink/message/fixed.rb +35 -25
- data/lib/slow_blink/message/floating_point.rb +16 -17
- data/lib/slow_blink/message/group.rb +242 -136
- data/lib/slow_blink/message/integer.rb +77 -51
- data/lib/slow_blink/message/model.rb +121 -85
- data/lib/slow_blink/message/sequence.rb +43 -28
- data/lib/slow_blink/message/string.rb +17 -31
- data/lib/slow_blink/message/time.rb +55 -17
- data/lib/slow_blink/message/time_of_day.rb +40 -50
- data/lib/slow_blink/schema.rb +17 -6
- data/lib/slow_blink/version.rb +1 -1
- data/rakefile +12 -7
- data/test/tc_incr_annote.rb +16 -0
- data/test/tc_inputs.rb +2 -2
- data/test/tc_model_dynamic_group.rb +105 -0
- data/test/tc_model_extension.rb +143 -0
- data/test/tc_model_static_group.rb +27 -36
- data/test/tc_model_think_blink.rb +18 -35
- metadata +12 -9
- data/ext/slow_blink/ext_compact_encoder/extconf.rb +0 -4
- /data/ext/slow_blink/{ext_compact_encoder → message/ext_compact_encoder}/compact_encoder.c +0 -0
- /data/ext/slow_blink/{ext_compact_encoder → message/ext_compact_encoder}/compact_encoder.h +0 -0
| @@ -19,99 +19,113 @@ | |
| 19 19 |  | 
| 20 20 | 
             
            module SlowBlink
         | 
| 21 21 |  | 
| 22 | 
            +
                # This module is concerned with generating models from Schema that are optimised for encoding/decoding and enforcing constraints
         | 
| 22 23 | 
             
                module Message
         | 
| 23 24 |  | 
| 24 | 
            -
                    class Error <  | 
| 25 | 
            +
                    class Error < StandardError
         | 
| 25 26 | 
             
                    end
         | 
| 26 27 |  | 
| 27 28 | 
             
                    class Model
         | 
| 28 29 |  | 
| 30 | 
            +
                        # the maximum level of nesting in messages able to be decoded by models
         | 
| 31 | 
            +
                        DEFAULT_MAX_RECURSION = 100
         | 
| 32 | 
            +
             | 
| 29 33 | 
             
                        # @api user
         | 
| 30 34 | 
             
                        #
         | 
| 31 35 | 
             
                        # Create a Model from a {Schema}
         | 
| 32 36 | 
             
                        #
         | 
| 33 37 | 
             
                        # @param schema [SlowBlink::Schema]
         | 
| 34 | 
            -
                         | 
| 38 | 
            +
                        # @param opts [Hash]
         | 
| 39 | 
            +
                        #
         | 
| 40 | 
            +
                        # @option opts [Symbol] :maxRecursion
         | 
| 41 | 
            +
                        #
         | 
| 42 | 
            +
                        def initialize(schema, **opts)
         | 
| 43 | 
            +
             | 
| 35 44 | 
             
                            @schema = schema
         | 
| 36 | 
            -
                            @taggedGroups = {} | 
| 37 | 
            -
                             | 
| 38 | 
            -
             | 
| 39 | 
            -
                             | 
| 45 | 
            +
                            @taggedGroups = {}
         | 
| 46 | 
            +
                            @groups = {}
         | 
| 47 | 
            +
                            @maxRecursion = opts[:maxRecursion]||DEFAULT_MAX_RECURSION
         | 
| 48 | 
            +
                            maxRecursion = @maxRecursion
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                            # define the extension object
         | 
| 51 | 
            +
                            groups = @groups
         | 
| 40 52 | 
             
                            taggedGroups = @taggedGroups
         | 
| 41 | 
            -
                             | 
| 53 | 
            +
                            permitted = schema.tagged.keys
         | 
| 54 | 
            +
                            @extensionObject = Class.new(DynamicGroup) do
         | 
| 55 | 
            +
                                @maxRecursion = maxRecursion
         | 
| 56 | 
            +
                                @extensionObject = self
         | 
| 42 57 | 
             
                                @opt = false
         | 
| 43 | 
            -
                                @groups =  | 
| 44 | 
            -
                                @ | 
| 45 | 
            -
             | 
| 58 | 
            +
                                @groups = groups
         | 
| 59 | 
            +
                                @taggedGroups = taggedGroups
         | 
| 60 | 
            +
                                @permitted = permitted                        
         | 
| 61 | 
            +
                            end
         | 
| 62 | 
            +
                            extensionObject = @extensionObject
         | 
| 63 | 
            +
                            
         | 
| 64 | 
            +
                            schema.groups.each do |name, g|
         | 
| 65 | 
            +
                                this = self
         | 
| 66 | 
            +
                                @groups[name] = Class.new(Group) do
         | 
| 67 | 
            +
                                    @extensionObject = extensionObject
         | 
| 68 | 
            +
                                    @maxRecursion = maxRecursion
         | 
| 69 | 
            +
                                    @name = g.nameWithID.name
         | 
| 70 | 
            +
                                    @id = g.nameWithID.id
         | 
| 71 | 
            +
                                    @fields = {}
         | 
| 72 | 
            +
                                    g.fields.each do |f|
         | 
| 73 | 
            +
                                        @fields[f.nameWithID.name] = this._model_field(f)
         | 
| 74 | 
            +
                                    end                   
         | 
| 75 | 
            +
                                end
         | 
| 76 | 
            +
                                if g.nameWithID.id
         | 
| 77 | 
            +
                                    @taggedGroups[g.nameWithID.id] = @groups[name]
         | 
| 78 | 
            +
                                end
         | 
| 79 | 
            +
                            end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
             | 
| 46 82 | 
             
                        end
         | 
| 47 83 |  | 
| 48 84 | 
             
                        # @api user
         | 
| 49 85 | 
             
                        #
         | 
| 50 86 | 
             
                        # Initialise a message model instance with a compact form string
         | 
| 51 87 | 
             
                        #
         | 
| 52 | 
            -
                        # @note return value will be an *anonymous* *subclass* *instance* of { | 
| 88 | 
            +
                        # @note return value will be an *anonymous* *subclass* *instance* of {Group}
         | 
| 53 89 | 
             
                        #
         | 
| 54 | 
            -
                        # @param [String] Blink Protocol compact form
         | 
| 55 | 
            -
                        # @return [ | 
| 90 | 
            +
                        # @param input [String] Blink Protocol compact form
         | 
| 91 | 
            +
                        # @return [Group] group instance
         | 
| 56 92 | 
             
                        #
         | 
| 57 93 | 
             
                        def decode_compact(input)
         | 
| 58 | 
            -
                             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
                        #   @param name [String] name of group
         | 
| 76 | 
            -
                        #   @yield [Group] group instance to initalise
         | 
| 77 | 
            -
                        #   @return [StaticGroup,DynamicGroup] group instance            
         | 
| 78 | 
            -
                        #
         | 
| 79 | 
            -
                        def group(name, data=nil, &block)
         | 
| 80 | 
            -
                            group = @top.groups.values.detect{|g|g.name == name}
         | 
| 81 | 
            -
                            if group                    
         | 
| 82 | 
            -
                                top = @top.new(group.new(data))
         | 
| 83 | 
            -
                                if block
         | 
| 84 | 
            -
                                    self.instance_exec(top, &block)
         | 
| 85 | 
            -
                                    # validate optional constraint here
         | 
| 94 | 
            +
                            stack = []
         | 
| 95 | 
            +
                            inputSize = input.size
         | 
| 96 | 
            +
                            buf = input.getBinary!
         | 
| 97 | 
            +
                            if buf.size > 0
         | 
| 98 | 
            +
                                id = buf.getU64!
         | 
| 99 | 
            +
                                groupClass = @taggedGroups[id]
         | 
| 100 | 
            +
                                begin
         | 
| 101 | 
            +
                                    if groupClass                        
         | 
| 102 | 
            +
                                        group = groupClass.from_compact!(buf, stack)                        
         | 
| 103 | 
            +
                                    else
         | 
| 104 | 
            +
                                        raise Error.new "W2: Group id #{group.id} is unknown"
         | 
| 105 | 
            +
                                    end
         | 
| 106 | 
            +
                                rescue Error => ex
         | 
| 107 | 
            +
                                    puts ex
         | 
| 108 | 
            +
                                    puts "encountered at offset #{inputSize - input.size}"
         | 
| 109 | 
            +
                                    puts stack.last.name
         | 
| 110 | 
            +
                                    raise
         | 
| 86 111 | 
             
                                end
         | 
| 87 | 
            -
                                top
         | 
| 88 112 | 
             
                            else
         | 
| 89 | 
            -
                                raise
         | 
| 113 | 
            +
                                raise Error.new "W1: Top level group cannot be null "                    
         | 
| 90 114 | 
             
                            end
         | 
| 91 115 | 
             
                        end
         | 
| 92 116 |  | 
| 93 | 
            -
                        # @api  | 
| 117 | 
            +
                        # @api user
         | 
| 94 118 | 
             
                        #
         | 
| 95 | 
            -
                        #  | 
| 119 | 
            +
                        # Get a group model
         | 
| 96 120 | 
             
                        #
         | 
| 97 | 
            -
                        # @param  | 
| 98 | 
            -
                        # @ | 
| 99 | 
            -
                        # @return [ | 
| 100 | 
            -
                         | 
| 101 | 
            -
             | 
| 102 | 
            -
                             | 
| 103 | 
            -
                                @implements = group.class
         | 
| 104 | 
            -
                                @name = group.nameWithID.name
         | 
| 105 | 
            -
                                @id = group.nameWithID.id
         | 
| 106 | 
            -
                                @opt = opt
         | 
| 107 | 
            -
                                @fields = group.fields.inject([]) do |fields, f|
         | 
| 108 | 
            -
                                    fields << this._model_field(f)                        
         | 
| 109 | 
            -
                                end
         | 
| 110 | 
            -
                                
         | 
| 111 | 
            -
                            end            
         | 
| 121 | 
            +
                        # @param name [String] name of group (may be qualified)
         | 
| 122 | 
            +
                        # @return [Class] {DynamicGroup} or {Group}
         | 
| 123 | 
            +
                        # @return [nil] group not defined
         | 
| 124 | 
            +
                        #
         | 
| 125 | 
            +
                        def group(name)
         | 
| 126 | 
            +
                            @groups[name]            
         | 
| 112 127 | 
             
                        end
         | 
| 113 128 |  | 
| 114 | 
            -
             | 
| 115 129 | 
             
                        # @api private
         | 
| 116 130 | 
             
                        #
         | 
| 117 131 | 
             
                        # Create a model for a Field
         | 
| @@ -121,11 +135,10 @@ module SlowBlink | |
| 121 135 | 
             
                        def _model_field(field)
         | 
| 122 136 | 
             
                            this = self
         | 
| 123 137 | 
             
                            Class.new(Field) do
         | 
| 124 | 
            -
                                @implements = field.class
         | 
| 125 138 | 
             
                                @opt = field.opt?
         | 
| 126 139 | 
             
                                @name = field.nameWithID.name
         | 
| 127 140 | 
             
                                @id = field.nameWithID.id
         | 
| 128 | 
            -
                                @type = this._model_type(field)
         | 
| 141 | 
            +
                                @type = this._model_type(field.type, field.opt?)
         | 
| 129 142 | 
             
                            end                
         | 
| 130 143 | 
             
                        end
         | 
| 131 144 |  | 
| @@ -133,50 +146,71 @@ module SlowBlink | |
| 133 146 | 
             
                        #
         | 
| 134 147 | 
             
                        # Create a model for a type
         | 
| 135 148 | 
             
                        #
         | 
| 136 | 
            -
                        # @param  | 
| 137 | 
            -
                        # @ | 
| 138 | 
            -
                         | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 149 | 
            +
                        # @param type [SlowBlink::Type] type definition
         | 
| 150 | 
            +
                        # @param opt  [true,false] parent definition may allow this type to be optional
         | 
| 151 | 
            +
                        #
         | 
| 152 | 
            +
                        #            
         | 
| 153 | 
            +
                        def _model_type(type, opt)
         | 
| 154 | 
            +
                            this = self
         | 
| 155 | 
            +
                            extensionObject = @extensionObject
         | 
| 156 | 
            +
                            maxRecursion = @maxRecursion
         | 
| 141 157 | 
             
                            case type.class
         | 
| 142 158 | 
             
                            when SlowBlink::OBJECT
         | 
| 143 159 | 
             
                                groups = @groups
         | 
| 144 | 
            -
                                 | 
| 145 | 
            -
                                 | 
| 146 | 
            -
             | 
| 147 | 
            -
                                    @ | 
| 160 | 
            +
                                taggedGroups = @taggedGroups
         | 
| 161 | 
            +
                                permitted = @taggedGroups.keys
         | 
| 162 | 
            +
                                Class.new(DynamicGroup) do
         | 
| 163 | 
            +
                                    @extensionObject = extensionObject
         | 
| 164 | 
            +
                                    @maxRecursion = maxRecursion
         | 
| 165 | 
            +
                                    @opt = opt
         | 
| 148 166 | 
             
                                    @groups = groups
         | 
| 167 | 
            +
                                    @taggedGroups = taggedGroups
         | 
| 149 168 | 
             
                                    @permitted = permitted                        
         | 
| 150 | 
            -
                                end | 
| 169 | 
            +
                                end                     
         | 
| 151 170 | 
             
                            when SlowBlink::REF
         | 
| 152 | 
            -
                                if type.ref.kind_of? Group
         | 
| 171 | 
            +
                                if type.ref.kind_of? SlowBlink::Group
         | 
| 153 172 | 
             
                                    if type.dynamic?
         | 
| 173 | 
            +
                                        taggedGroups = @taggedGroups
         | 
| 154 174 | 
             
                                        groups = @groups
         | 
| 155 | 
            -
                                        permitted = @ | 
| 175 | 
            +
                                        permitted = @taggedGroups.keys
         | 
| 156 176 | 
             
                                        @schema.tagged.each do |id, g|
         | 
| 157 177 | 
             
                                            if g.group_kind_of?(type)
         | 
| 158 178 | 
             
                                                permitted << id
         | 
| 159 179 | 
             
                                            end
         | 
| 160 180 | 
             
                                        end
         | 
| 161 181 | 
             
                                        Class.new(DynamicGroup) do
         | 
| 162 | 
            -
                                            @ | 
| 163 | 
            -
                                            @ | 
| 164 | 
            -
                                            @opt =  | 
| 182 | 
            +
                                            @extensionObject = extensionObject
         | 
| 183 | 
            +
                                            @maxRecursion = maxRecursion
         | 
| 184 | 
            +
                                            @opt = opt
         | 
| 185 | 
            +
                                            @taggedGroups = taggedGroups
         | 
| 165 186 | 
             
                                            @groups = groups
         | 
| 166 187 | 
             
                                            @permitted = permitted
         | 
| 167 188 | 
             
                                        end                               
         | 
| 168 189 | 
             
                                    else
         | 
| 169 | 
            -
                                         | 
| 190 | 
            +
                                        Class.new(StaticGroup) do
         | 
| 191 | 
            +
                                            @extensionObject = extensionObject
         | 
| 192 | 
            +
                                            @maxRecursion = maxRecursion
         | 
| 193 | 
            +
                                            @name = type.ref.nameWithID.name
         | 
| 194 | 
            +
                                            @id = nil
         | 
| 195 | 
            +
                                            @opt = opt
         | 
| 196 | 
            +
                                            @fields = {}
         | 
| 197 | 
            +
                                            type.ref.fields.each do |f|
         | 
| 198 | 
            +
                                                @fields[f.nameWithID.name] = this._model_field(f)
         | 
| 199 | 
            +
                                            end                                                            
         | 
| 200 | 
            +
                                        end                                                   
         | 
| 170 201 | 
             
                                    end
         | 
| 171 202 | 
             
                                else
         | 
| 172 | 
            -
                                    _model_type( | 
| 203 | 
            +
                                    _model_type(type.ref)
         | 
| 173 204 | 
             
                                end
         | 
| 174 | 
            -
                             | 
| 205 | 
            +
                            when SlowBlink::SEQUENCE
         | 
| 206 | 
            +
                                Class.new(SEQUENCE) do
         | 
| 207 | 
            +
                                    @maxRecursion = maxRecursion
         | 
| 208 | 
            +
                                    @type = this._model_type(type.type, false)
         | 
| 209 | 
            +
                                end                    
         | 
| 210 | 
            +
                            else
         | 
| 175 211 | 
             
                                Class.new(SlowBlink::Message.const_get(type.class.name.split('::').last)) do
         | 
| 176 | 
            -
                                     | 
| 177 | 
            -
                                    @ | 
| 178 | 
            -
                                    @name = name
         | 
| 179 | 
            -
                                    @type = type                        
         | 
| 212 | 
            +
                                    @opt = opt
         | 
| 213 | 
            +
                                    @type = type                                        
         | 
| 180 214 | 
             
                                end                    
         | 
| 181 215 | 
             
                            end
         | 
| 182 216 | 
             
                        end
         | 
| @@ -187,6 +221,7 @@ module SlowBlink | |
| 187 221 |  | 
| 188 222 | 
             
            end
         | 
| 189 223 |  | 
| 224 | 
            +
            require "slow_blink/message/ext_compact_encoder"
         | 
| 190 225 | 
             
            require "slow_blink/message/field"
         | 
| 191 226 | 
             
            require "slow_blink/message/integer"
         | 
| 192 227 | 
             
            require "slow_blink/message/string"
         | 
| @@ -200,4 +235,5 @@ require "slow_blink/message/group" | |
| 200 235 | 
             
            require "slow_blink/message/time"
         | 
| 201 236 | 
             
            require "slow_blink/message/time_of_day"
         | 
| 202 237 | 
             
            require "slow_blink/message/date"
         | 
| 238 | 
            +
            require "slow_blink/message/decimal"
         | 
| 203 239 |  | 
| @@ -20,56 +20,71 @@ | |
| 20 20 | 
             
            module SlowBlink::Message
         | 
| 21 21 |  | 
| 22 22 | 
             
                class SEQUENCE
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    # @return the type that repeats in this SEQUENCE
         | 
| 25 | 
            +
                    def self.type
         | 
| 26 | 
            +
                        @type
         | 
| 27 | 
            +
                    end
         | 
| 23 28 |  | 
| 24 | 
            -
                    def self.from_compact!(input)
         | 
| 29 | 
            +
                    def self.from_compact!(input, stack)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                        if stack.size < @maxRecursion
         | 
| 32 | 
            +
                            stack << self
         | 
| 33 | 
            +
                        else
         | 
| 34 | 
            +
                            raise Error.new "stack limit"
         | 
| 35 | 
            +
                        end
         | 
| 36 | 
            +
                    
         | 
| 25 37 | 
             
                        value = []
         | 
| 26 38 | 
             
                        size = input.getU32!
         | 
| 27 39 | 
             
                        if size
         | 
| 28 40 | 
             
                            while out.size < size do
         | 
| 29 | 
            -
                                value << @type.from_compact!(input)
         | 
| 41 | 
            +
                                value << @type.from_compact!(input, stack)
         | 
| 30 42 | 
             
                            end
         | 
| 31 43 | 
             
                            self.new(value)
         | 
| 32 44 | 
             
                        else
         | 
| 33 | 
            -
                             | 
| 45 | 
            +
                            nil
         | 
| 34 46 | 
             
                        end
         | 
| 35 | 
            -
                    end
         | 
| 36 47 |  | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
                        if value
         | 
| 40 | 
            -
                            if value.kind_of? Array
         | 
| 41 | 
            -
                                @value = v
         | 
| 42 | 
            -
                            else
         | 
| 43 | 
            -
                                raise Error.new "bad type"
         | 
| 44 | 
            -
                            end
         | 
| 45 | 
            -
                        elsif self.class.opt?
         | 
| 46 | 
            -
                            @value = nil
         | 
| 47 | 
            -
                        else
         | 
| 48 | 
            -
                            raise Error.new "value unacceptable"
         | 
| 49 | 
            -
                        end
         | 
| 48 | 
            +
                        stack.pop
         | 
| 49 | 
            +
                        
         | 
| 50 50 | 
             
                    end
         | 
| 51 51 |  | 
| 52 | 
            +
                    # @return [Array]
         | 
| 52 53 | 
             
                    def get
         | 
| 53 54 | 
             
                        @value
         | 
| 54 55 | 
             
                    end
         | 
| 55 56 |  | 
| 57 | 
            +
                    # Set value of a SEQUENCE
         | 
| 58 | 
            +
                    #
         | 
| 56 59 | 
             
                    # @param value [Array]
         | 
| 57 | 
            -
                     | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            +
                    # @raise [TypeError]
         | 
| 61 | 
            +
                    # @raise [RangeError]
         | 
| 62 | 
            +
                    #
         | 
| 63 | 
            +
                    def set(value)
         | 
| 64 | 
            +
                        @value = []
         | 
| 65 | 
            +
                        if value.kind_of? Array
         | 
| 66 | 
            +
                            value.each do |v|
         | 
| 67 | 
            +
                                if v.kind_of? self.class.type
         | 
| 68 | 
            +
                                    @value << v
         | 
| 69 | 
            +
                                else
         | 
| 70 | 
            +
                                    @value << self.class.type.new(v)
         | 
| 71 | 
            +
                                end
         | 
| 72 | 
            +
                            end
         | 
| 60 73 | 
             
                        else
         | 
| 61 | 
            -
                             | 
| 74 | 
            +
                            raise TypeError.new "expecting an array"
         | 
| 62 75 | 
             
                        end
         | 
| 63 76 | 
             
                    end
         | 
| 64 77 |  | 
| 78 | 
            +
                    # @note calls {#set}(value)
         | 
| 79 | 
            +
                    def initialize(value)       
         | 
| 80 | 
            +
                        set(value)            
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 65 83 | 
             
                    def to_compact(out)
         | 
| 66 | 
            -
                         | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
                        else
         | 
| 71 | 
            -
                            out.putU32(nil)
         | 
| 72 | 
            -
                        end                
         | 
| 84 | 
            +
                        out.putU32(@value.size)
         | 
| 85 | 
            +
                        @value.each do |value|                                
         | 
| 86 | 
            +
                            value.to_compact(out)
         | 
| 87 | 
            +
                        end            
         | 
| 73 88 | 
             
                    end
         | 
| 74 89 |  | 
| 75 90 | 
             
                end
         | 
| @@ -21,15 +21,7 @@ module SlowBlink::Message | |
| 21 21 |  | 
| 22 22 | 
             
                class STRING
         | 
| 23 23 |  | 
| 24 | 
            -
                    def self. | 
| 25 | 
            -
                        @name
         | 
| 26 | 
            -
                    end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                    def self.opt?
         | 
| 29 | 
            -
                        @opt
         | 
| 30 | 
            -
                    end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                    def self.from_compact!(input)
         | 
| 24 | 
            +
                    def self.from_compact!(input, stack)
         | 
| 33 25 | 
             
                        value = input.getString!
         | 
| 34 26 | 
             
                        if value
         | 
| 35 27 | 
             
                            if !@type.size or value.size <= @type.size
         | 
| @@ -37,52 +29,46 @@ module SlowBlink::Message | |
| 37 29 | 
             
                            else
         | 
| 38 30 | 
             
                                raise Error.new "W7: String value exceeds maximum size"
         | 
| 39 31 | 
             
                            end
         | 
| 40 | 
            -
                        elsif @opt
         | 
| 41 | 
            -
                            self.new(nil)
         | 
| 42 32 | 
             
                        else
         | 
| 43 | 
            -
                             | 
| 44 | 
            -
                        end
         | 
| 33 | 
            +
                            value
         | 
| 34 | 
            +
                        end               
         | 
| 45 35 | 
             
                    end
         | 
| 46 36 |  | 
| 37 | 
            +
                    # @return [Integer,nil] maximum size of string in bytes
         | 
| 47 38 | 
             
                    def self.size
         | 
| 48 39 | 
             
                        @type.size
         | 
| 49 40 | 
             
                    end
         | 
| 50 41 |  | 
| 42 | 
            +
                    # @return [String]
         | 
| 51 43 | 
             
                    def get
         | 
| 52 44 | 
             
                        @value
         | 
| 53 45 | 
             
                    end
         | 
| 54 46 |  | 
| 47 | 
            +
                    # Set a string value
         | 
| 48 | 
            +
                    # @param value [String]
         | 
| 49 | 
            +
                    # @raise [TypeError]
         | 
| 50 | 
            +
                    # @raise [RangeError] value is larger than {::size}
         | 
| 55 51 | 
             
                    def set(value)
         | 
| 56 | 
            -
                        if value
         | 
| 57 | 
            -
                            if value. | 
| 58 | 
            -
                                 | 
| 59 | 
            -
                                    @value = value
         | 
| 60 | 
            -
                                else
         | 
| 61 | 
            -
                                    raise Error.new "string cannot be larger than #{self.class.size} bytes"
         | 
| 62 | 
            -
                                end
         | 
| 52 | 
            +
                        if value.kind_of? String 
         | 
| 53 | 
            +
                            if !self.class.size or value.size <= self.class.size
         | 
| 54 | 
            +
                                @value = value.to_s
         | 
| 63 55 | 
             
                            else
         | 
| 64 | 
            -
                                raise  | 
| 56 | 
            +
                                raise RangeError.new "String instance cannot be larger than #{self.class.size} bytes"
         | 
| 65 57 | 
             
                            end
         | 
| 66 | 
            -
                        elsif self.class.opt?
         | 
| 67 | 
            -
                            @value = nil
         | 
| 68 58 | 
             
                        else
         | 
| 69 | 
            -
                            raise  | 
| 70 | 
            -
                        end
         | 
| 59 | 
            +
                            raise TypeError.new "expecting a String instance"
         | 
| 60 | 
            +
                        end            
         | 
| 71 61 | 
             
                    end
         | 
| 72 62 |  | 
| 63 | 
            +
                    # @note calls {#set}(value)
         | 
| 73 64 | 
             
                    def initialize(value)
         | 
| 74 | 
            -
                         | 
| 75 | 
            -
                        if value
         | 
| 76 | 
            -
                            set(value)
         | 
| 77 | 
            -
                        end
         | 
| 65 | 
            +
                        set(value)
         | 
| 78 66 | 
             
                    end
         | 
| 79 67 |  | 
| 80 68 | 
             
                    def to_compact(out)
         | 
| 81 69 | 
             
                        out.putString(@value)
         | 
| 82 70 | 
             
                    end
         | 
| 83 71 |  | 
| 84 | 
            -
                    
         | 
| 85 | 
            -
                    
         | 
| 86 72 | 
             
                end
         | 
| 87 73 |  | 
| 88 74 | 
             
            end
         | 
| @@ -17,46 +17,84 @@ | |
| 17 17 | 
             
            # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
         | 
| 18 18 | 
             
            # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| 19 19 |  | 
| 20 | 
            -
             | 
| 20 | 
            +
            require 'time'
         | 
| 21 21 |  | 
| 22 | 
            +
            module SlowBlink::Message
         | 
| 22 23 |  | 
| 23 24 | 
             
                class MILLI_TIME
         | 
| 24 25 |  | 
| 25 | 
            -
                    def self.from_compact!(input)
         | 
| 26 | 
            -
                         | 
| 27 | 
            -
                    end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                    def set(value)
         | 
| 26 | 
            +
                    def self.from_compact!(input, stack)
         | 
| 27 | 
            +
                        value = input.getI64!
         | 
| 30 28 | 
             
                        if value
         | 
| 31 | 
            -
                             | 
| 32 | 
            -
                        elsif self.class.opt?
         | 
| 33 | 
            -
                            @value = nil
         | 
| 29 | 
            +
                            self.new(value)
         | 
| 34 30 | 
             
                        else
         | 
| 35 | 
            -
                             | 
| 31 | 
            +
                            value
         | 
| 36 32 | 
             
                        end
         | 
| 37 33 | 
             
                    end
         | 
| 38 34 |  | 
| 35 | 
            +
                    # @return [DateTime]
         | 
| 39 36 | 
             
                    def get
         | 
| 40 37 | 
             
                        @value
         | 
| 41 38 | 
             
                    end
         | 
| 42 39 |  | 
| 43 | 
            -
                     | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 40 | 
            +
                    # @overload set(value)
         | 
| 41 | 
            +
                    #   Set a millisecond time value
         | 
| 42 | 
            +
                    #   @param value [Time,DateTime,Date]
         | 
| 43 | 
            +
                    # @overload set(value)
         | 
| 44 | 
            +
                    #   @param value [String] time in ISO 8601
         | 
| 45 | 
            +
                    # @overload set(value)
         | 
| 46 | 
            +
                    #   @param value [String] time in milliseconds since UNIX epoch
         | 
| 47 | 
            +
                    # @raise [TypeError]
         | 
| 48 | 
            +
                    # @raise [ArgumentError]
         | 
| 49 | 
            +
                    def set(value)            
         | 
| 50 | 
            +
                        if value.kind_of? Time or value.kind_of? DateTime or value.kind_of? Date
         | 
| 51 | 
            +
                            @value = time.to_datetime
         | 
| 52 | 
            +
                        elsif value.kind_of? String
         | 
| 53 | 
            +
                            @value = DateTime.parse(value)
         | 
| 54 | 
            +
                        elsif value.kind_of? Integer
         | 
| 55 | 
            +
                            @value = DateTime.new(value)
         | 
| 46 56 | 
             
                        else
         | 
| 47 | 
            -
                             | 
| 48 | 
            -
                        end
         | 
| 57 | 
            +
                            raise TypeError
         | 
| 58 | 
            +
                        end                        
         | 
| 49 59 | 
             
                    end
         | 
| 50 60 |  | 
| 51 | 
            -
                     | 
| 52 | 
            -
             | 
| 61 | 
            +
                    # @note calls {#set}(value)
         | 
| 62 | 
            +
                    def initialize(value)
         | 
| 63 | 
            +
                        set(value)            
         | 
| 53 64 | 
             
                    end
         | 
| 54 65 |  | 
| 66 | 
            +
                    def to_compact(out)
         | 
| 67 | 
            +
                        out.putI64(@value.strftime('%Q'))            
         | 
| 68 | 
            +
                    end
         | 
| 55 69 |  | 
| 56 70 | 
             
                end
         | 
| 57 71 |  | 
| 58 72 | 
             
                class NANO_TIME < MILLI_TIME
         | 
| 59 73 |  | 
| 74 | 
            +
                    def to_compact(out)
         | 
| 75 | 
            +
                        out.putI64(@value.strftime('%N'))
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    # @overload set(value)
         | 
| 79 | 
            +
                    #   Set a nanosecond resolution time value
         | 
| 80 | 
            +
                    #   @param value [Time,DateTime,Date]
         | 
| 81 | 
            +
                    # @overload set(value)
         | 
| 82 | 
            +
                    #   @param value [String] time in ISO 8601
         | 
| 83 | 
            +
                    # @overload set(value)
         | 
| 84 | 
            +
                    #   @param value [Integer] time in nanoseconds since UNIX epoch
         | 
| 85 | 
            +
                    # @raise [TypeError]
         | 
| 86 | 
            +
                    # @raise [ArgumentError]
         | 
| 87 | 
            +
                    def set(value)            
         | 
| 88 | 
            +
                        if value.kind_of? Time or value.kind_of? DateTime or value.kind_of? Date
         | 
| 89 | 
            +
                            @value = time.to_datetime
         | 
| 90 | 
            +
                        elsif value.kind_of? String
         | 
| 91 | 
            +
                            @value = DateTime.parse(value)
         | 
| 92 | 
            +
                        elsif value.kind_of? Integer
         | 
| 93 | 
            +
                            @value = DateTime.new(value)
         | 
| 94 | 
            +
                        else
         | 
| 95 | 
            +
                            raise TypeError
         | 
| 96 | 
            +
                        end                        
         | 
| 97 | 
            +
                    end
         | 
| 60 98 |  | 
| 61 99 | 
             
                end
         | 
| 62 100 |  |