def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
      if map.is_a? Hash
        scale_x = map.fetch(:scale_x, 1)
        scale_y = map.fetch(:scale_y, 1)
        alpha = map.fetch(:alpha, 0xff)
        color = map.fetch(:color, 0xffffff)
        angle = map.fetch(:angle, nil)
        flip = map.fetch(:flip, nil)
        z_index = map.fetch(:z_index, 0)
        round = map.fetch(:round, false)
        map = map.fetch(:map, nil)
      end

      color = (alpha << 24) | color
      if angle
        @img[@img_index].draw_rot @x - (map ? map.cam.x : 0) + @img[0].width * scale_x * 0.5,
                                  @y - (map ? map.cam.y : 0) + @img[0].height * scale_y * 0.5,
                                  z_index, angle, 0.5, 0.5, (flip == :horiz ? -scale_x : scale_x),
                                  (flip == :vert ? -scale_y : scale_y), color
      else
        x = @x - (map ? map.cam.x : 0) + (flip == :horiz ? scale_x * @img[0].width : 0)
        y = @y - (map ? map.cam.y : 0) + (flip == :vert ? scale_y * @img[0].height : 0)
        @img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
                              z_index, (flip == :horiz ? -scale_x : scale_x),
                              (flip == :vert ? -scale_y : scale_y), color
      end
    end

def draw ( map = nil , scale_x = 1 , scale_y = 1 , alpha = 0xff , color = 0xffffff , angle = nil , flip = nil , z_index = 0 , round = false ) if map . is_a? Hash scale_x = map . fetch ( :scale_x , 1 ) scale_y = map . fetch ( :scale_y , 1 ) alpha = map . fetch ( :alpha , 0xff ) color = map . fetch ( :color , 0xffffff ) angle = map . fetch ( :angle , nil ) flip = map . fetch ( :flip , nil ) z_index = map . fetch ( :z_index , 0 ) round = map . fetch ( :round , false ) map = map . fetch ( :map , nil ) end color = ( alpha << 24 ) | color if angle @img [ @img_index ] . draw_rot @x - ( map ? map . cam . x : 0 ) + @img [ 0 ] . width * scale_x * 0.5 , @y - ( map ? map . cam . y : 0 ) + @img [ 0 ] . height * scale_y * 0.5 , z_index , angle , 0.5 , 0.5 , ( flip == :horiz ? - scale_x : scale_x ) , ( flip == :vert ? - scale_y : scale_y ) , color else x = @x - ( map ? map . cam . x : 0 ) + ( flip == :horiz ? scale_x * @img [ 0 ] . width : 0 ) y = @y - ( map ? map . cam . y : 0 ) + ( flip == :vert ? scale_y * @img [ 0 ] . height : 0 ) @img [ @img_index ] . draw ( round ? x . round : x ) , ( round ? y . round : y ) , z_index , ( flip == :horiz ? - scale_x : scale_x ) , ( flip == :vert ? - scale_y : scale_y ) , color end end

Draws the sprite in the screen

 Parameters:
 [map] A Map object, relative to which the sprite will be drawn (the x
       and y coordinates of the sprite will be changed according to the
       position of the camera).
 [scale_x] A scale factor to be applied horizontally to the image.
 [scale_y] A scale factor to be applied vertically to the image.
 [alpha] The opacity with which the image will be drawn. Valid values
         vary from 0 (fully transparent) to 255 (fully opaque).
 [color] A color filter to apply to the image. A white (0xffffff) filter
         will keep all colors unchanged, while a black (0x000000) filter
         will turn all colors to black. A red (0xff0000) filter will keep
         reddish colors with slight or no change, whereas bluish colors
         will be darkened, for example.
 [angle] A rotation, in degrees, to be applied to the image, relative to
         its center.
 [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
        to draw it vertically flipped.
 [z_index] The z-order to draw the object. Objects with larger z-orders
           will be drawn on top of the ones with smaller z-orders.
 [round] Specify whether the drawing coordinates should be rounded to an
         integer before drawing, to avoid little distortions of the image.
         Only applies when the image is not rotated.

 *Obs.:* This method accepts named parameters.


def update
      return unless @enabled and @visible

      mouse_over = Mouse.over? @x, @y, @w, @h
      mouse_press = Mouse.button_pressed? :left
      mouse_rel = Mouse.button_released? :left

      if @state == :up
        if mouse_over
          @img_index = 1
          @state = :over
        else
          @img_index = 0
        end
      elsif @state == :over
        if not mouse_over
          @img_index = 0
          @state = :up
        elsif mouse_press
          @img_index = 2
          @state = :down
        else
          @img_index = 1
        end
      elsif @state == :down
        if not mouse_over
          @img_index = 0
          @state = :down_out
        elsif mouse_rel
          @img_index = 1
          @state = :over
          click
        else
          @img_index = 2
        end
      else # :down_out
        if mouse_over
          @img_index = 2
          @state = :down
        elsif mouse_rel
          @img_index = 0
          @state = :up
        else
          @img_index = 0
        end
      end
    end

Creates a button.

 Parameters:
 [x] The x-coordinate where the button will be drawn in the screen.
 [y] The y-coordinate where the button will be drawn in the screen.
 [font] The Gosu::Font object that will be used to draw the
        button text.
 [text] The button text. Can be +nil+ or empty.
 [img] A spritesheet containing four images in a column, representing,
       from top to bottom, the default state, the hover state (when the
       mouse is over the button), the pressed state (when the mouse
       button is down and the cursor is over the button) and the disabled
       state. If +nil+, the +width+ and +height+ parameters must be
       provided.
 [text_color] Color of the button text, in hexadecimal RRGGBB format.
 [disabled_text_color] Color of the button text, when it's disabled, in
                       hexadecimal RRGGBB format.
 [over_text_color] Color of the button text, when the cursor is over it
                   (hexadecimal RRGGBB).
 [down_text_color] Color of the button text, when it is pressed
                   (hexadecimal RRGGBB).
 [center_x] Whether the button text should be horizontally centered in its
            area (the area is defined by the image size, if an image is
            given, or by the +width+ and +height+ parameters, otherwise).
 [center_y] Whether the button text should be vertically centered in its
            area (the area is defined by the image size, if an image is
            given, or by the +width+ and +height+ parameters, otherwise).
 [margin_x] The x offset, from the button x-coordinate, to draw the text.
            This parameter is used only if +center+ is false.
 [margin_y] The y offset, from the button y-coordinate, to draw the text.
            This parameter is used only if +center+ is false.
 [width] Width of the button clickable area. This parameter is used only
         if +img+ is +nil+.
 [height] Height of the button clickable area. This parameter is used
          only if +img+ is +nil+.
 [params] An object containing any parameters you want passed to the
          +action+ block. When the button is clicked, the following is
          called:
            @action.call @params
          Note that this doesn't force you to declare a block that takes
          parameters.
 [retro] Whether the image should be loaded with the 'retro' option set
         (see +Gosu::Image+ for details). If the value is omitted, the
         +Res.retro_images+ value will be used.
 [scale_x] Horizontal scale to draw the component with.
 [scale_y] Vertical scale to draw the component with.
 [anchor] See parameter with the same name in Panel#initialize for details.
 [action] The block of code executed when the button is clicked (or by
          calling the +click+ method).

 *Obs.:* This method accepts named parameters, but +x+ and +y+ are
 mandatory (also, +img+ is mandatory when +width+ and +height+ are not
 provided, and vice-versa).
 Updates the button, checking the mouse movement and buttons to define
 the button state.


def draw(alpha = 0xff, z_index = 0, color = 0xffffff)
      return unless @visible

      color = (alpha << 24) | color
      text_color =
        if @enabled
          if @state == :down
            @down_text_color
          else
            @state == :over ? @over_text_color : @text_color
          end
        else
          @disabled_text_color
        end
      text_color = (alpha << 24) | text_color
      @img[@img_index].draw @x, @y, z_index, @scale_x, @scale_y, color if @img
      if @text
        if @center_x or @center_y
          rel_x = @center_x ? 0.5 : 0
          rel_y = @center_y ? 0.5 : 0
          @font.draw_text_rel @text, @text_x, @text_y, z_index, rel_x, rel_y, @scale_x, @scale_y, text_color
        else
          @font.draw_text @text, @text_x, @text_y, z_index, @scale_x, @scale_y, text_color
        end
      end
    end

Draws the button in the screen.

 Parameters:
 [alpha] The opacity with which the button will be drawn. Allowed values
         vary between 0 (fully transparent) and 255 (fully opaque).
 [z_index] The z-order to draw the object. Objects with larger z-orders
           will be drawn on top of the ones with smaller z-orders.
 [color] Color to apply a filter to the image.


def text=(value, trigger_changed = true)
      @text = value[0...@max_length]
      @nodes.clear; @nodes << @text_x
      x = @nodes[0]
      @text.chars.each { |char|
        x += @font.text_width(char) * @scale_x
        @nodes << x
      }
      @cur_node = @nodes.size - 1
      @anchor1 = nil
      @anchor2 = nil
      set_cursor_visible
      @on_text_changed.call @text, @params if trigger_changed && @on_text_changed
    end

def text = ( value , trigger_changed = true ) @text = value [ 0 ... @max_length ] @nodes . clear ; @nodes << @text_x x = @nodes [ 0 ] @text . chars . each { | char | x += @font . text_width ( char ) * @scale_x @nodes << x } @cur_node = @nodes . size - 1 @anchor1 = nil @anchor2 = nil set_cursor_visible @on_text_changed . call @text , @params if trigger_changed && @on_text_changed end

Sets the text of the text field to the specified value.

 Parameters:
 [value] The new text to be set. If it's longer than the +max_length+
         parameter used in the constructor, it will be truncated to
         +max_length+ characters.

Sets the text of the text field to the specified value .


def set_position(x, y)
      d_x = x - @x
      d_y = y - @y
      @x = x; @y = y
      @text_x += d_x
      @text_y += d_y
      @nodes.map! do |n|
        n + d_x
      end
    end

def set_position ( x , y ) d_x = x - @x d_y = y - @y @x = x ; @y = y @text_x += d_x @text_y += d_y @nodes . map! do | n | n + d_x end end

Sets the position of the text field in the screen.

 Parameters:
 [x] The new x-coordinate for the text field.
 [y] The new y-coordinate for the text field.

Sets the position of the text field in the screen .


def draw(alpha = 0xff, z_index = 0, color = 0xffffff, disabled_color = 0x808080)
      return unless @visible

      color = (alpha << 24) | ((@enabled or @disabled_img) ? color : disabled_color)
      text_color = (alpha << 24) | (@enabled ? @text_color : @disabled_text_color)
      img = ((@enabled or @disabled_img.nil?) ? @img : @disabled_img)
      img.draw @x, @y, z_index, @scale_x, @scale_y, color
      @font.draw_text @text, @text_x, @text_y, z_index, @scale_x, @scale_y, text_color

      if @anchor1 and @anchor2
        selection_color = ((alpha / 2) << 24) | @selection_color
        G.window.draw_quad @nodes[@anchor1], @text_y, selection_color,
                           @nodes[@anchor2] + 1, @text_y, selection_color,
                           @nodes[@anchor2] + 1, @text_y + @font.height * @scale_y, selection_color,
                           @nodes[@anchor1], @text_y + @font.height * @scale_y, selection_color, z_index
      end

      if @cursor_visible
        if @cursor_img
          @cursor_img.draw @nodes[@cur_node] - (@cursor_img.width * @scale_x) / 2, @text_y, z_index, @scale_x, @scale_y
        else
          cursor_color = alpha << 24
          G.window.draw_quad @nodes[@cur_node], @text_y, cursor_color,
                             @nodes[@cur_node] + 1, @text_y, cursor_color,
                             @nodes[@cur_node] + 1, @text_y + @font.height * @scale_y, cursor_color,
                             @nodes[@cur_node], @text_y + @font.height * @scale_y, cursor_color, z_index
        end
      end
    end

Draws the text field in the screen.

 Parameters:
 [alpha] The opacity with which the text field will be drawn. Allowed
         values vary between 0 (fully transparent) and 255 (fully opaque).
 [z_index] The z-order to draw the object. Objects with larger z-orders
           will be drawn on top of the ones with smaller z-orders.
 [color] Color to apply a filter to the image.
 [disabled_color] Color to apply a filter to the image when the field is
                  disabled.


def draw(alpha = 0xff, z_index = 0, color = 0xffffff)
      return unless @visible

      if @bg
        c = (alpha << 24) | color
        @bg.draw @x, @y, z_index, @scale_x, @scale_y, c
      else
        c = (alpha << 24) | @bg_color
        G.window.draw_quad @x, @y, c,
                           @x + @w, @y, c,
                           @x + @w, @y + @h, c,
                           @x, @y + @h, c, z_index
      end
      if @fg
        c = (alpha << 24) | color
        w1 = @fg.width * @scale_x
        w2 = (@value.to_f / @max_value * @w).round
        x0 = @x + @fg_margin_x
        x = 0
        while x <= w2 - w1
          @fg.draw x0 + x, @y + @fg_margin_y, z_index, @scale_x, @scale_y, c
          x += w1
        end
        if w2 - x > 0
          img = Gosu::Image.new(@fg_path, tileable: true, retro: @retro, rect: [0, 0, ((w2 - x) / @scale_x).round, @fg.height])
          img.draw x0 + x, @y + @fg_margin_y, z_index, @scale_x, @scale_y, c
        end
      else
        c = (alpha << 24) | @fg_color
        rect_r = @x + (@value.to_f / @max_value * @w).round
        G.window.draw_quad @x, @y, c,
                           rect_r, @y, c,
                           rect_r, @y + @h, c,
                           @x, @y + @h, c, z_index
      end
      if @font
        c = (alpha << 24) | @text_color
        @text = @format == '%' ? "#{(@value.to_f / @max_value * 100).round}%" : "#{@value}/#{@max_value}"
        @font.draw_text_rel @text, @x + @w / 2, @y + @h / 2, z_index, 0.5, 0.5, @scale_x, @scale_y, c
      end
    end

Draws the progress bar.

 Parameters:
 [alpha] (+Fixnum+) The opacity with which the progress bar will be drawn.
         Allowed values vary between 0 (fully transparent) and 255 (fully
         opaque).
 [z_index] (+Fixnum+) The z-order to draw the object. Objects with larger
           z-orders will be drawn on top of the ones with smaller z-orders.
 [color] Color to apply a filter to the images (when these are provided).


def update
      return unless @enabled and @visible
      if @open and Mouse.button_pressed? :left and not Mouse.over?(@x, @y, @w, @max_h)
        toggle
        return
      end
      @buttons.each { |b| b.update }
    end

def update return unless @enabled and @visible if @open and Mouse . button_pressed? :left and not Mouse . over? ( @x , @y , @w , @max_h ) toggle return end @buttons . each { | b | b . update } end

Creates a new drop-down list.

 Parameters:
 [x] The x-coordinate of the object.
 [y] The y-coordinate of the object.
 [font] Font to be used by the buttons that compose the drop-down list.
 [img] Image of the main button, i.e., the one at the top, that toggles
       visibility of the other buttons (the "option" buttons).
 [opt_img] Image for the "option" buttons, as described above.
 [options] Array of available options for this control (+String+s).
 [option] Index of the firstly selected option.
 [text_margin] Left margin of the text inside the buttons (vertically, the
               text will always be centered).
 [width] Width of the control, used when no image is provided.
 [height] Height of the control, used when no image is provided.
 [text_color] Used as the +text_color+ parameter in the constructor of the
              buttons.
 [disabled_text_color] Analogous to +text_color+.
 [over_text_color] Same as above.
 [down_text_color] Same as above.
 [retro] Whether the images should be loaded with the 'retro' option set
         (see +Gosu::Image+ for details). If the value is omitted, the
         +Res.retro_images+ value will be used.
 [scale_x] Horizontal scale to draw the component with.
 [scale_y] Vertical scale to draw the component with.
 [anchor] See parameter with the same name in Panel#initialize for details.
 [on_changed] Action performed when the value of the dropdown is changed.
              It must be a block with two parameters, which will receive
              the old and the new value, respectively.

 *Obs.:* This method accepts named parameters, but +x+, +y+, +font+ and
 +options+ are mandatory (also, +img+ and +opt_img+ are mandatory when
 +width+ and +height+ are not provided, and vice-versa).
 Updates the control.


def value=(val)
      if @options.include? val
        old = @value
        @value = @buttons[0].text = val
        @on_changed.call(old, val) if @on_changed
      end
    end

def value = ( val ) if @options . include? val old = @value @value = @buttons [ 0 ] . text = val @on_changed . call ( old , val ) if @on_changed end end

Sets the currently selected value of the drop-down list. It is ignored if
 it is not among the available options.

Sets the currently selected value of the drop - down list . It is ignored if it is not among the available options .


def draw(alpha = 0xff, z_index = 0, color = 0xffffff, over_color = 0xcccccc)
      return unless @visible
      unless @img
        bottom = @y + (@open ? @max_h : @h) + @scale_y
        b_color = (alpha << 24)
        G.window.draw_quad @x - @scale_x, @y - @scale_y, b_color,
                           @x + @w + @scale_x, @y - @scale_y, b_color,
                           @x + @w + @scale_x, bottom, b_color,
                           @x - @scale_x, bottom, b_color, z_index
        @buttons.each do |b|
          c = (alpha << 24) | (b.state == :over ? over_color : color)
          G.window.draw_quad b.x, b.y, c,
                             b.x + b.w, b.y, c,
                             b.x + b.w, b.y + b.h, c,
                             b.x, b.y + b.h, c, z_index + 1 if b.visible
        end
      end
      @buttons[0].draw(alpha, z_index, color)
      @buttons[1..-1].each { |b| b.draw alpha, z_index + 1, color }
    end

def draw ( alpha = 0xff , z_index = 0 , color = 0xffffff , over_color = 0xcccccc ) return unless @visible unless @img bottom = @y + ( @open ? @max_h : @h ) + @scale_y b_color = ( alpha << 24 ) G . window . draw_quad @x - @scale_x , @y - @scale_y , b_color , @x + @w + @scale_x , @y - @scale_y , b_color , @x + @w + @scale_x , bottom , b_color , @x - @scale_x , bottom , b_color , z_index @buttons . each do | b | c = ( alpha << 24 ) | ( b . state == :over ? over_color : color ) G . window . draw_quad b . x , b . y , c , b . x + b . w , b . y , c , b . x + b . w , b . y + b . h , c , b . x , b . y + b . h , c , z_index + 1 if b . visible end end @buttons [ 0 ] . draw ( alpha , z_index , color ) @buttons [ 1 .. - 1 ] . each { | b | b . draw alpha , z_index + 1 , color } end

Draws the drop-down list.

 Parameters:
 [alpha] (+Fixnum+) The opacity with which the drop-down list will be
         drawn. Allowed values vary between 0 (fully transparent) and 255
         (fully opaque).
 [z_index] (+Fixnum+) The z-order to draw the object. Objects with larger
           z-orders will be drawn on top of the ones with smaller z-orders.
 [color] Color of the buttons, if no image was provided, or color to apply
         a filter to the images.
 [over_color] Color of the buttons when the mouse is over them (when no
              image was provided).


def draw(alpha = 255, z_index = 0, color = 0xffffff)
      c = @enabled ? @text_color : @disabled_text_color
      r1 = c >> 16
      g1 = (c & 0xff00) >> 8
      b1 = (c & 0xff)
      r2 = color >> 16
      g2 = (color & 0xff00) >> 8
      b2 = (color & 0xff)
      r1 *= r2; r1 /= 255
      g1 *= g2; g1 /= 255
      b1 *= b2; b1 /= 255
      color = (alpha << 24) | (r1 << 16) | (g1 << 8) | b1
      @font.draw_text(@text, @x, @y, z_index, @scale_x, @scale_y, color)
    end

Creates a new label.

 Parameters:
 [x] The x-coordinate of the label.
 [y] The x-coordinate of the label.
 [font] Font that will be used to draw the label's text.
 [text] The label's text.
 [text_color] The default text color.
 [disabled_text_color] The text color when the label is disabled.
 [scale_x] The horizontal scale factor.
 [scale_y] The vertical scale factor.
 [anchor] See parameter with the same name in Panel#initialize for details.
 Draws the label.

 Parameters:
 [alpha] The opacity with which the label will be drawn. Allowed values
         vary between 0 (fully transparent) and 255 (fully opaque).
 [z_index] The z-order to draw the object. Objects with larger z-orders
           will be drawn on top of the ones with smaller z-orders.
 [color] Color to apply a filter to the text.


def write_line(text, x = nil, y = nil, mode = :left, color = 0, alpha = 0xff,
                   effect = nil, effect_color = 0, effect_size = 1, effect_alpha = 0xff,
                   z_index = 0)
      if text.is_a? Hash
        x = text[:x]
        y = text[:y]
        mode = text.fetch(:mode, :left)
        color = text.fetch(:color, 0)
        alpha = text.fetch(:alpha, 0xff)
        effect = text.fetch(:effect, nil)
        effect_color = text.fetch(:effect_color, 0)
        effect_size = text.fetch(:effect_size, 1)
        effect_alpha = text.fetch(:effect_alpha, 0xff)
        z_index = text.fetch(:z_index, 0)
        text = text[:text]
      end

      color = (alpha << 24) | color
      rel =
        case mode
        when :left then 0
        when :center then 0.5
        when :right then 1
        else 0
        end
      if effect
        effect_color = (effect_alpha << 24) | effect_color
        if effect == :border
          @font.draw_markup_rel text, x - effect_size, y - effect_size, z_index, rel, 0, 1, 1, effect_color
          @font.draw_markup_rel text, x, y - effect_size, z_index, rel, 0, 1, 1, effect_color
          @font.draw_markup_rel text, x + effect_size, y - effect_size, z_index, rel, 0, 1, 1, effect_color
          @font.draw_markup_rel text, x + effect_size, y, z_index, rel, 0, 1, 1, effect_color
          @font.draw_markup_rel text, x + effect_size, y + effect_size, z_index, rel, 0, 1, 1, effect_color
          @font.draw_markup_rel text, x, y + effect_size, z_index, rel, 0, 1, 1, effect_color
          @font.draw_markup_rel text, x - effect_size, y + effect_size, z_index, rel, 0, 1, 1, effect_color
          @font.draw_markup_rel text, x - effect_size, y, z_index, rel, 0, 1, 1, effect_color
        elsif effect == :shadow
          @font.draw_markup_rel text, x + effect_size, y + effect_size, z_index, rel, 0, 1, 1, effect_color
        end
      end
      @font.draw_markup_rel text, x, y, z_index, rel, 0, 1, 1, color
    end

def write_line ( text , x = nil , y = nil , mode = :left , color = 0 , alpha = 0xff , effect = nil , effect_color = 0 , effect_size = 1 , effect_alpha = 0xff , z_index = 0 ) if text . is_a? Hash x = text [ :x ] y = text [ :y ] mode = text . fetch ( :mode , :left ) color = text . fetch ( :color , 0 ) alpha = text . fetch ( :alpha , 0xff ) effect = text . fetch ( :effect , nil ) effect_color = text . fetch ( :effect_color , 0 ) effect_size = text . fetch ( :effect_size , 1 ) effect_alpha = text . fetch ( :effect_alpha , 0xff ) z_index = text . fetch ( :z_index , 0 ) text = text [ :text ] end color = ( alpha << 24 ) | color rel = case mode when :left then 0 when :center then 0.5 when :right then 1 else 0 end if effect effect_color = ( effect_alpha << 24 ) | effect_color if effect == :border @font . draw_markup_rel text , x - effect_size , y - effect_size , z_index , rel , 0 , 1 , 1 , effect_color @font . draw_markup_rel text , x , y - effect_size , z_index , rel , 0 , 1 , 1 , effect_color @font . draw_markup_rel text , x + effect_size , y - effect_size , z_index , rel , 0 , 1 , 1 , effect_color @font . draw_markup_rel text , x + effect_size , y , z_index , rel , 0 , 1 , 1 , effect_color @font . draw_markup_rel text , x + effect_size , y + effect_size , z_index , rel , 0 , 1 , 1 , effect_color @font . draw_markup_rel text , x , y + effect_size , z_index , rel , 0 , 1 , 1 , effect_color @font . draw_markup_rel text , x - effect_size , y + effect_size , z_index , rel , 0 , 1 , 1 , effect_color @font . draw_markup_rel text , x - effect_size , y , z_index , rel , 0 , 1 , 1 , effect_color elsif effect == :shadow @font . draw_markup_rel text , x + effect_size , y + effect_size , z_index , rel , 0 , 1 , 1 , effect_color end end @font . draw_markup_rel text , x , y , z_index , rel , 0 , 1 , 1 , color end

Creates a TextHelper.

 Parameters:
 [font] A Gosu::Font that will be used to draw the text.
 [line_spacing] When drawing multiple lines, the distance, in pixels,
                between each line.
 Draws a single line of text.

 Parameters:
 [text] The text to be drawn. No line breaks are allowed. You can use the
        `<b>` tag for bold, `<i>` for italic and `<c=rrggbb>` for colors.
 [x] The horizontal reference for drawing the text. If +mode+ is +:left+,
     all text will be drawn from this point to the right; if +mode+ is
     +:right+, all text will be drawn from this point to the left; and if
     +mode+ is +:center+, the text will be equally distributed to the
     left and to the right of this point.
 [y] The vertical reference for drawing the text. All text will be drawn
     from this point down.
 [mode] The alignment of the text. Valid values are +:left+, +:right+ and
        +:center+.
 [color] The color of the text, in hexadecimal RRGGBB format.
 [alpha] The opacity of the text. Valid values vary from 0 (fully
         transparent) to 255 (fully opaque).
 [effect] Effect to add to the text. It can be either +nil+, for no effect,
          +:border+ for bordered text, or +:shadow+ for shadowed text (the
          shadow is always placed below and to the right of the text).
 [effect_color] Color of the effect, if any.
 [effect_size] Size of the effect, if any. In the case of +:border+, this
               will be the width of the border (the border will only look
               good when +effect_size+ is relatively small, compared to the
               size of the font); in the case of +:shadow+, it will be the
               distance between the text and the shadow.
 [effect_alpha] Opacity of the effect, if any. For shadows, it is usual to
                provide less than 255.
 [z_index] The z-order to draw the object. Objects with larger z-orders
           will be drawn on top of the ones with smaller z-orders.

 *Obs.:* This method accepts named parameters, but +text+, +x+ and +y+ are
 mandatory.


def write_breaking(text, x, y, width, mode = :left, color = 0, alpha = 0xff, z_index = 0)
      color = (alpha << 24) | color
      text.split("\n").each do |p|
        if mode == :justified
          y = write_paragraph_justified p, x, y, width, color, z_index
        else
          rel =
            case mode
            when :left then 0
            when :center then 0.5
            when :right then 1
            else 0
            end
          y = write_paragraph p, x, y, width, rel, color, z_index
        end
      end
    end

def write_breaking ( text , x , y , width , mode = :left , color = 0 , alpha = 0xff , z_index = 0 ) color = ( alpha << 24 ) | color text . split ( "\n" ) . each do | p | if mode == :justified y = write_paragraph_justified p , x , y , width , color , z_index else rel = case mode when :left then 0 when :center then 0.5 when :right then 1 else 0 end y = write_paragraph p , x , y , width , rel , color , z_index end end end

Draws text, breaking lines when needed and when explicitly caused by the
 "\n" character.

 Parameters:
 [text] The text to be drawn. Line breaks are allowed. You can use the
        `<b>` tag for bold, `<i>` for italic and `<c=rrggbb>` for colors.
 [x] The horizontal reference for drawing the text. Works like in
     +write_line+ for the +:left+, +:right+ and +:center+ modes. For the
     +:justified+ mode, works the same as for +:left+.
 [y] The vertical reference for drawing the text. All text will be drawn
     from this point down.
 [width] The maximum width for the lines of text. Line is broken when
         this width is exceeded.
 [mode] The alignment of the text. Valid values are +:left+, +:right+,
        +:center+ and +:justified+.
 [color] The color of the text, in hexadecimal RRGGBB format.
 [alpha] The opacity of the text. Valid values vary from 0 (fully
         transparent) to 255 (fully opaque).
 [z_index] The z-order to draw the object. Objects with larger z-orders
           will be drawn on top of the ones with smaller z-orders.

Draws text breaking lines when needed and when explicitly caused by the \ n character .


def add_global(message)
      unless (slot = @entries.index { |e| e.nil? })
        # No more free slots. We have to find the least recently used one.
        slot = 0
        0.upto(15) do |i|
          if i != slot && @entries[slot].last_use > @entries[i].last_use
            slot = i
          end
        end
      end
      @entries[slot] = Entry.new(message, Time.now)

      slot
    end

Add a new GlobalFitMessage to the mapper and return the local message
 number.

Add a new GlobalFitMessage to the mapper and return the local message number .


def get_local(message)
      0.upto(15) do |i|
        if (entry = @entries[i]) && entry.global_message == message
          entry.last_use = Time.now
          return i
        end
      end
      nil
    end

def get_local ( message ) 0 . upto ( 15 ) do | i | if ( entry = @entries [ i ] ) && entry . global_message == message entry . last_use = Time . now return i end end nil end

Get the local message number for a given GlobalFitMessage. If there is
 no message number, nil is returned.

Get the local message number for a given GlobalFitMessage . If there is no message number nil is returned .


def check
      last_timestamp = ts_16_offset = nil
      last_ts_16 = nil

      # The timestamp_16 is a 2 byte time stamp value that is used instead of
      # the 4 byte timestamp field for monitoring records that have
      # current_activity_type_intensity values with an activity type of 6. The
      # value seems to be in seconds, but the 0 value reference does not seem
      # to be included in the file. However, it can be approximated using the
      # surrounding timestamp values.
      @monitorings.each do |record|
        if last_ts_16 && ts_16_offset && record.timestamp_16 &&
           record.timestamp_16 < last_ts_16
          # Detect timestamp_16 wrap-arounds. timestamp_16 is a 16 bit value.
          # In case of a wrap-around we adjust the ts_16_offset accordingly.
          ts_16_offset += 2 ** 16
        end
        if ts_16_offset
          # We have already found the offset. Adjust all timestamps according
          # to 'offset + timestamp_16'
          if record.timestamp_16
            record.timestamp = ts_16_offset + record.timestamp_16
            last_ts_16 = record.timestamp_16
          end
        else
          # We are still looking for the offset.
          if record.timestamp_16 && last_timestamp
            # We have a previous timestamp and found the first record with a
            # timestamp_16 value set. We assume that the timestamp of this
            # record is one minute after the previously found timestamp.
            # That's just a guess. Who knows what the Garmin engineers were
            # thinking here?
            ts_16_offset = last_timestamp + 60 - record.timestamp_16
            record.timestamp = ts_16_offset + record.timestamp_16
            last_ts_16 = record.timestamp_16
          else
            # Just save the timestamp of the current record.
            last_timestamp = record.timestamp
          end
        end
      end
    end

Create a new Monitoring_B object.
 @param field_values [Hash] A Hash that provides initial values for
        certain fields of the FitDataRecord.
 Perform some basic logical checks on the object and all references sub
 objects. Any errors will be reported via the Log object.


def create_global_definition(fit_entity)
      messages = fit_entity.developer_fit_messages
      unless (gfm = GlobalFitMessages[@native_mesg_num])
        Log.error "Developer field description references unknown global " +
          "message number #{@native_mesg_num}"
        return
      end

      if @developer_data_index >=
           fit_entity.top_level_record.developer_data_ids.size
         Log.error "Developer data index #{@developer_data_index} is too large"
         return
      end

      msg = messages[@native_mesg_num] ||
        messages.message(@native_mesg_num, gfm.name)
      unless (@fit_base_type_id & 0x7F) < FIT_TYPE_DEFS.size
        Log.error "fit_base_type_id #{@fit_base_type_id} is too large"
        return
      end
      options = {}
      options[:scale] = @scale if @scale
      options[:offset] = @offset if @offset
      options[:array] = @array if @array
      options[:unit] = @units
      msg.field(@field_definition_number,
                FIT_TYPE_DEFS[@fit_base_type_id & 0x7F][1],
                "_#{@developer_data_index}_#{@field_name}", options)
    end

Create a new FieldDescription object.
 @param field_values [Hash] Hash that provides initial values for certain
        fields.


def check(index)
      unless @device_index
        Log.fatal 'device info record must have a device_index'
      end
      if @device_index == 0
        unless @manufacturer
          Log.fatal 'device info record 0 must have a manufacturer field set'
        end
        if @manufacturer == 'garmin'
          unless @garmin_product
            Log.fatal 'device info record 0 must have a garman_product ' +
                      'field set'
          end
        else
          unless @product
            Log.fatal 'device info record 0 must have a product field set'
          end
        end
        if @serial_number.nil?
          Log.fatal 'device info record 0 must have a serial number set'
        end
      end
    end

Ensure that FitDataRecords have a deterministic sequence. Device infos
 are sorted by device_index.

Ensure that FitDataRecords have a deterministic sequence . Device infos are sorted by device_index .


def open(io)
      begin
        @@logger = Logger.new(io)
      rescue => e
        @@logger = Logger.new($stderr)
        Log.fatal "Cannot open log file: #{e.message}"
      end
    end

def
open
(
io
)
begin
@@logger
=
Logger
.
new
(
io
)
rescue
=>
e
@@logger
=
Logger
.
new
(
$stderr
)
Log
.
fatal
"Cannot open log file: #{e.message}"
end
end

Redirect all log messages to the given IO.
 @param io [IO] Output file descriptor

Redirect all log messages to the given IO .


def set_type(type)
      if @top_level_record
        Log.fatal "FIT file type has already been set to " +
                  "#{@top_level_record.class}"
      end
      case type
      when 4, 'activity'
        @top_level_record = Activity.new
        @type = 'activity'
      when 32, 'monitoring_b'
        @top_level_record = Monitoring_B.new
        @type = 'monitoring_b'
      when 44, 'metrics'
        @top_level_record = Metrics.new
        @type = 'metrics'
      else
        Log.error "Unsupported FIT file type #{type}"
        return nil
      end

      @top_level_record
    end

Create a FitFileEntity.
 Set what kind of FIT file we are dealing with.
 @return The Ruby object that will hold the content of the FIT file. It's
 a derivative of FitDataRecord.

Create a FitFileEntity . Set what kind of FIT file we are dealing with .


def check
      unless @timestamp && @timestamp >= Time.parse('1990-01-01T00:00:00+00:00')
        Log.fatal "Activity has no valid timestamp"
      end
      unless @total_timer_time
        Log.fatal "Activity has no valid total_timer_time"
      end
      unless @device_infos.length > 0
        Log.fatal "Activity must have at least one device_info section"
      end
      @device_infos.each.with_index { |d, index| d.check(index) }
      @sensor_settings.each.with_index { |s, index| s.check(index) }
      unless @num_sessions == @sessions.count
        Log.fatal "Activity record requires #{@num_sessions}, but "
                  "#{@sessions.length} session records were found in the "
                  "FIT file."
      end

      # Records must have consecutively growing timestamps and distances.
      ts = Time.parse('1989-12-31')
      distance = nil
      invalid_records = []
      @records.each_with_index do |r, idx|
        Log.fatal "Record has no timestamp" unless r.timestamp
        if r.timestamp < ts
          Log.fatal "Record has earlier timestamp than previous record"
        end
        if r.distance
          if distance && r.distance < distance
            # Normally this should be a fatal error as the FIT file is clearly
            # broken. Unfortunately, the Skiing/Boarding app in the Fenix3
            # produces such broken FIT files. So we just warn about this
            # problem and discard the earlier records.
            Log.error "Record #{r.timestamp} has smaller distance " +
                      "(#{r.distance}) than an earlier record (#{distance})."
            # Index of the list record to be discarded.
            (idx - 1).downto(0) do |i|
              if (ri = @records[i]).distance > r.distance
                # This is just an approximation. It looks like the app adds
                # records to the FIT file for runs that it meant to discard.
                # Maybe the two successive time start events are a better
                # criteria. But this workaround works for now.
                invalid_records << ri
              else
                # All broken records have been found.
                break
              end
            end
          end
          distance = r.distance
        end
        ts = r.timestamp
      end
      unless invalid_records.empty?
        # Delete all the broken records from the @records Array.
        Log.warn "Discarding #{invalid_records.length} earlier records"
        @records.delete_if { |r| invalid_records.include?(r) }
      end

      # Laps must have a consecutively growing message index.
      @laps.each.with_index do |lap, index|
        lap.check(index)
        # If we have heart rate zone records, there should be one for each
        # lap
        @heart_rate_zones[index].check(index) if @heart_rate_zones[index]
      end
      @sessions.each { |s| s.check(self) }
    end

Create a new Activity object.
 @param field_values [Hash] A Hash that provides initial values for
        certain fields of the FitDataRecord.
 Perform some basic logical checks on the object and all references sub
 objects. Any errors will be reported via the Log object.


def total_gps_distance
      timer_stops = []
      # Generate a list of all timestamps where the timer was stopped.
      @events.each do |e|
        if e.event == 'timer' && e.event_type == 'stop_all'
          timer_stops << e.timestamp
        end
      end

      # The first record of a FIT file can already have a distance associated
      # with it. The GPS location of the first record is not where the start
      # button was pressed. This introduces a slight inaccurcy when computing
      # the total distance purely on the GPS coordinates found in the records.
      d = 0.0
      last_lat = last_long = nil
      last_timestamp = nil

      # Iterate over all the records and accumlate the distances between the
      # neiboring coordinates.
      @records.each do |r|
        if (lat = r.position_lat) && (long = r.position_long)
          if last_lat && last_long
            distance = Fit4Ruby::GeoMath.distance(last_lat, last_long,
                                                  lat, long)
            d += distance
          end
          if last_timestamp
            speed = distance / (r.timestamp - last_timestamp)
          end
          if timer_stops[0] == r.timestamp
            # If a stop event was found for this record timestamp we clear the
            # last_* values so that the distance covered while being stopped
            # is not added to the total.
            last_lat = last_long = nil
            last_timestamp = nil
            timer_stops.shift
          else
            last_lat = lat
            last_long = long
            last_timestamp = r.timestamp
          end
        end
      end
      d
    end

Total distance convered by this activity purely computed by the GPS
 coordinates. This may differ from the distance computed by the device as
 it can be based on a purely calibrated footpod.

Total distance convered by this activity purely computed by the GPS coordinates . This may differ from the distance computed by the device as it can be based on a purely calibrated footpod .


def vo2max
      # First check the event log for a vo2max reporting event.
      @events.each do |e|
        return e.vo2max if e.event == 'vo2max'
      end
      # Then check the user_data entries for a metmax entry. METmax * 3.5
      # is same value as VO2max.
      @user_data.each do |u|
        return u.metmax * 3.5 if u.metmax
      end

      nil
    end

Returns the computed VO2max value. This value is computed by the device
 based on multiple previous activities.

Returns the computed VO2max value . This value is computed by the device based on multiple previous activities .


def write(io, id_mapper)
      @file_id.write(io, id_mapper)
      @file_creator.write(io, id_mapper)

      (@field_descriptions + @developer_data_ids +
       @device_infos + @sensor_settings +
       @data_sources + @user_profiles +
       @physiological_metrics + @events +
       @sessions + @laps + @records + @heart_rate_zones +
       @personal_records).sort.each do |s|
        s.write(io, id_mapper)
      end
      super
    end

def write ( io , id_mapper ) @file_id . write ( io , id_mapper ) @file_creator . write ( io , id_mapper ) ( @field_descriptions + @developer_data_ids + @device_infos + @sensor_settings + @data_sources + @user_profiles + @physiological_metrics + @events + @sessions + @laps + @records + @heart_rate_zones + @personal_records ) . sort . each do | s | s . write ( io , id_mapper ) end super end

Write the Activity data to a file.
 @param io [IO] File reference
 @param id_mapper [FitMessageIdMapper] Maps global FIT record types to
        local ones.


def new_fit_data_record(record_type, field_values = {})
      case record_type
      when 'file_id'
        @file_id = (record = FileId.new(field_values))
      when 'field_description'
        @field_descriptions << (record = FieldDescription.new(field_values))
      when 'developer_data_id'
        @developer_data_ids << (record = DeveloperDataId.new(field_values))
      when 'epo_data'
        @epo_data = (record = EPO_Data.new(field_values))
      when 'file_creator'
        @file_creator = (record = FileCreator.new(field_values))
      when 'device_info'
        @device_infos << (record = DeviceInfo.new(field_values))
      when 'sensor_settings'
        @sensor_settings << (record = SensorSettings.new(field_values))
      when 'data_sources'
        @data_sources << (record = DataSources.new(field_values))
      when 'user_data'
        @user_data << (record = UserData.new(field_values))
      when 'user_profile'
        @user_profiles << (record = UserProfile.new(field_values))
      when 'physiological_metrics'
        @physiological_metrics <<
          (record = PhysiologicalMetrics.new(field_values))
      when 'event'
        @events << (record = Event.new(field_values))
      when 'session'
        unless @cur_lap_records.empty?
          # Copy selected fields from section to lap.
          lap_field_values = {}
          [ :timestamp, :sport ].each do |f|
            lap_field_values[f] = field_values[f] if field_values.include?(f)
          end
          # Ensure that all previous records have been assigned to a lap.
          record = create_new_lap(lap_field_values)
        end
        @num_sessions += 1
        @sessions << (record = Session.new(@cur_session_laps, @lap_counter,
                                           field_values))
        @cur_session_laps = []
      when 'lap'
        record = create_new_lap(field_values)
      when 'record'
        @cur_lap_records << (record = Record.new(field_values))
        @records << record
      when 'hrv'
        @hrv << (record = HRV.new(field_values))
      when 'heart_rate_zones'
        @heart_rate_zones << (record = HeartRateZones.new(field_values))
      when 'personal_records'
        @personal_records << (record = PersonalRecords.new(field_values))
      else
        record = nil
      end

      record
    end

Check if the current Activity is equal to the passed Activity.
 @param a [Activity] Activity to compare this Activity with.
 @return [TrueClass/FalseClass] true if both Activities are equal,
 otherwise false.
 Create a new FitDataRecord.
 @param record_type [String] Type that identifies the FitDataRecord
        derived class to create.
 @param field_values [Hash] A Hash that provides initial values for
        certain fields of the FitDataRecord.
 @return FitDataRecord

Check if the current Activity is equal to the passed Activity .


def check(activity)
      unless @first_lap_index
        Log.fatal 'first_lap_index is not set'
      end
      unless @num_laps
        Log.fatal 'num_laps is not set'
      end
      @first_lap_index.upto(@first_lap_index - @num_laps) do |i|
        if (lap = activity.lap[i])
          @laps << lap
        else
          Log.fatal "Session references lap #{i} which is not contained in "
                    "the FIT file."
        end
      end
    end

Create a new Session object.
 @param laps [Array of Laps] Laps to associate with the Session.
 @param first_lap_index [Fixnum] Index of the first Lap in this Session.
 @param field_values [Hash] Hash that provides initial values for certain
        fields.
 Perform some basic consistency and logical checks on the object. Errors
 are reported via the Log object.


def field(number, type, name, opts = {})
      field = Field.new(type, name, opts)
      register_field_by_name(field, name)
      register_field_by_number(field, number)
    end

def field ( number , type , name , opts = { } ) field = Field . new ( type , name , opts ) register_field_by_name ( field , name ) register_field_by_number ( field , number ) end

Create a new GlobalFitMessage definition.
 @param name [String] name of the FIT message
 @param number [Fixnum] global message number
 Two GlobalFitMessage objects are considered equal if they have the same
 number, name and list of named fields.
 Define a new Field for this message definition.

Create a new GlobalFitMessage definition .


def alt_field(number, ref_field, &block)
      unless @fields_by_name.include?(ref_field)
        raise "Unknown ref_field: #{ref_field}"
      end

      field = AltField.new(self, ref_field, &block)
      register_field_by_number(field, number)
    end

def
alt_field
(
number
,
ref_field
,
&
block
)
unless
@fields_by_name
.
include?
(
ref_field
)
raise
"Unknown ref_field: #{ref_field}"
end
field
=
AltField
.
new
(
self
,
ref_field
,
block
)
register_field_by_number
(
field
,
number
)
end

Define a new set of Field alternatives for this message definition.

Define a new set of Field alternatives for this message definition .


def spam?
      self.class.mail_captcha.each do |field|
        next if send(field).blank?

        if defined?(Rails) && Rails.env.development?
          raise ScriptError, "The captcha field #{field} was supposed to be blank"
        else
          return true
        end
      end

      false
    end

In development, raises an error if the captcha field is not blank. This is
 is good to remember that the field should be hidden with CSS and shown only
 to robots.

 In test and in production, it returns true if all captcha fields are blank,
 returns false otherwise.

In development raises an error if the captcha field is not blank . This is is good to remember that the field should be hidden with CSS and shown only to robots .


def deliver!
      mailer = MailForm::Notifier.contact(self)
      if mailer.respond_to?(:deliver_now)
        mailer.deliver_now
      else
        mailer.deliver
      end
    end

def deliver! mailer = MailForm :: Notifier . contact ( self ) if mailer . respond_to? ( :deliver_now ) mailer . deliver_now else mailer . deliver end end

Deliver the resource without running any validation.

Deliver the resource without running any validation .


def mail_form_attributes
      self.class.mail_attributes.each_with_object({}) do |attr, hash|
        hash[attr.to_s] = send(attr)
      end
    end

def mail_form_attributes self . class . mail_attributes . each_with_object ( { } ) do | attr , hash | hash [ attr . to_s ] = send ( attr ) end end

Returns a hash of attributes, according to the attributes existent in
 self.class.mail_attributes.

Returns a hash of attributes according to the attributes existent in self . class . mail_attributes .


def start
      extract_and_configure
      if config.managed?
        exec('start', p: port, c: config.cloud)

        # Wait for solr to start
        unless status
          sleep config.poll_interval
        end

        after_start
      end
    end

def
start
extract_and_configure
if
config
.
managed?
exec
(
'start'
,
p
:
port
,
c
:
config
.
cloud
)
# Wait for solr to start
unless
status
sleep
config
.
poll_interval
end
after_start
end
end

Start Solr and wait for it to become available


def restart
      if config.managed? && started?
        exec('restart', p: port, c: config.cloud)
      end
    end

def restart if config . managed? && started? exec ( 'restart' , p : port , c : config . cloud ) end end

Stop Solr and wait for it to finish exiting


def create(options = {})
      options[:name] ||= SecureRandom.hex

      create_options = { p: port }
      create_options[:c] = options[:name] if options[:name]
      create_options[:n] = options[:config_name] if options[:config_name]
      create_options[:d] = options[:dir] if options[:dir]

      Retriable.retriable do
        raise "Not started yet" unless started?
      end

      # short-circuit if we're using persisted data with an existing core/collection
      return if options[:persist] && create_options[:c] && client.exists?(create_options[:c])

      exec("create", create_options)

      options[:name]
    end

Create a new collection in solr
 @param [Hash] options
 @option options [String] :name
 @option options [String] :dir


def upconfig(options = {})
      options[:name] ||= SecureRandom.hex
      options[:zkhost] ||= zkhost

      upconfig_options = { upconfig: true, n: options[:name] }
      upconfig_options[:d] = options[:dir] if options[:dir]
      upconfig_options[:z] = options[:zkhost] if options[:zkhost]

      exec 'zk', upconfig_options

      options[:name]
    end

def upconfig ( options = { } ) options [ :name ] ||= SecureRandom . hex options [ :zkhost ] ||= zkhost upconfig_options = { upconfig : true , n : options [ :name ] } upconfig_options [ :d ] = options [ :dir ] if options [ :dir ] upconfig_options [ :z ] = options [ :zkhost ] if options [ :zkhost ] exec 'zk' , upconfig_options options [ :name ] end

Update the collection configuration in zookeeper
 @param [Hash] options
 @option options [String] :config_name
 @option options [String] :dir


def downconfig(options = {})
      options[:name] ||= SecureRandom.hex
      options[:zkhost] ||= zkhost

      downconfig_options = { downconfig: true, n: options[:name] }
      downconfig_options[:d] = options[:dir] if options[:dir]
      downconfig_options[:z] = options[:zkhost] if options[:zkhost]

      exec 'zk', downconfig_options

      options[:name]
    end

def downconfig ( options = { } ) options [ :name ] ||= SecureRandom . hex options [ :zkhost ] ||= zkhost downconfig_options = { downconfig : true , n : options [ :name ] } downconfig_options [ :d ] = options [ :dir ] if options [ :dir ] downconfig_options [ :z ] = options [ :zkhost ] if options [ :zkhost ] exec 'zk' , downconfig_options options [ :name ] end

Copy the collection configuration from zookeeper to a local directory
 @param [Hash] options
 @option options [String] :config_name
 @option options [String] :dir


def with_collection(options = {})
      options = config.collection_options.merge(options)
      return yield if options.empty?

      name = create(options)
      begin
        yield name
      ensure
        delete name unless options[:persist]
      end
    end

def with_collection ( options = { } ) options = config . collection_options . merge ( options ) return yield if options . empty? name = create ( options ) begin yield name ensure delete name unless options [ :persist ] end end

Create a new collection, run the block, and then clean up the collection
 @param [Hash] options
 @option options [String] :name
 @option options [String] :dir

Create a new collection run the block and then clean up the collection


def clean!
      stop
      remove_instance_dir!
      FileUtils.remove_entry(config.download_dir, true) if File.exist?(config.download_dir)
      FileUtils.remove_entry(config.tmp_save_dir, true) if File.exist? config.tmp_save_dir
      checksum_validator.clean!
      FileUtils.remove_entry(config.version_file) if File.exist? config.version_file
    end

def clean! stop remove_instance_dir! FileUtils . remove_entry ( config . download_dir , true ) if File . exist? ( config . download_dir ) FileUtils . remove_entry ( config . tmp_save_dir , true ) if File . exist? config . tmp_save_dir checksum_validator . clean! FileUtils . remove_entry ( config . version_file ) if File . exist? config . version_file end

Clean up any files solr_wrapper may have downloaded


def get_signals
      all_signals = []
      current = @klass
      while current != Qt::Base
        meta = Meta[current.name]
        if !meta.nil?
          all_signals.concat meta.signals
        end
        current = current.superclass
      end
      return all_signals
    end

def get_signals all_signals = [ ] current = @klass while current != Qt :: Base meta = Meta [ current . name ] if ! meta . nil? all_signals . concat meta . signals end current = current . superclass end return all_signals end

Return a list of signals, including inherited ones

Return a list of signals including inherited ones


def +(other)
      if Duration === other
        Duration.new(value + other.value, @parts + other.parts)
      else
        Duration.new(value + other, @parts + [[:seconds, other]])
      end
    end

def + ( other ) if Duration === other Duration . new ( value + other . value , @parts + other . parts ) else Duration . new ( value + other , @parts + [ [ :seconds , other ] ] ) end end

Adds another Duration or a Numeric to this Duration. Numeric values
 are treated as seconds.

Adds another Duration or a Numeric to this Duration . Numeric values are treated as seconds .


def days_to_week_start(start_day = Date.beginning_of_week)
      start_day_number = DAYS_INTO_WEEK[start_day]
      current_day_number = wday != 0 ? wday - 1 : 6
      (current_day_number - start_day_number) % 7
    end

Returns the number of days to the start of the week on the given day.
 Week is assumed to start on +start_day+, default is
 +Date.beginning_of_week+ or +config.beginning_of_week+ when set.

Returns the number of days to the start of the week on the given day . Week is assumed to start on + start_day + default is + Date . beginning_of_week + or + config . beginning_of_week + when set .


def reset
      @width             = 0 if no_width
      @render_period     = frequency == 0 ? 0 : 1.0 / frequency
      @current           = 0
      @last_render_time  = Time.now
      @last_render_width = 0
      @done              = false
      @stopped           = false
      @start_at          = Time.now
      @started           = false
      @tokens            = {}

      @meter.clear
    end

Create progress bar

 @param [String] format
   the tokenized string that displays the output

 @param [Hash] options
 @option options [Numeric] :total
   the total number of steps to completion
 @option options [Numeric] :width
   the maximum width for the bars display including
   all formatting options
 @option options [Boolean] :no_width
   true when progression is unknown defaulting to false
 @option options [Boolean] :clear
   whether or not to clear the progress line
 @option options [Boolean] :hide_cursor
   display or hide cursor
 @option options [Object] :output
   the object that responds to print call defaulting to stderr
 @option options [Number] :frequency
   the frequency with which to display bars
 @option options [Number] :interval
   the period for sampling of speed measurement

 @api public
 Reset progress to default configuration

 @api public


def advance(progress = 1, tokens = {})
      return if done?

      synchronize do
        emit(:progress, progress)
        if progress.respond_to?(:to_hash)
          tokens, progress = progress, 1
        end
        @start_at  = Time.now if @current.zero? && !@started
        @current  += progress
        @tokens    = tokens
        @meter.sample(Time.now, progress)

        if !no_width && @current >= total
          finish && return
        end

        now = Time.now
        return if (now - @last_render_time) < @render_period
        render
      end
    end

def advance ( progress = 1 , tokens = { } ) return if done? synchronize do emit ( :progress , progress ) if progress . respond_to? ( :to_hash ) tokens , progress = progress , 1 end @start_at = Time . now if @current . zero? && ! @started @current += progress @tokens = tokens @meter . sample ( Time . now , progress ) if ! no_width && @current >= total finish && return end now = Time . now return if ( now - @last_render_time ) < @render_period render end end

Advance the progress bar

 @param [Object|Number] progress

 @api public


def iterate(collection, progress = 1, &block)
      update(total: collection.count * progress) unless total
      progress_enum = Enumerator.new do |iter|
        collection.each do |elem|
          advance(progress)
          iter.yield(elem)
        end
      end
      block_given? ? progress_enum.each(&block) : progress_enum
    end

def iterate ( collection , progress = 1 , & block ) update ( total : collection . count * progress ) unless total progress_enum = Enumerator . new do | iter | collection . each do | elem | advance ( progress ) iter . yield ( elem ) end end block_given? ? progress_enum . each ( block ) : progress_enum end

Iterate over collection either yielding computation to block
 or provided Enumerator. If the bar's `total` was not set,
 it would be taken from `collection.count`, otherwise previously
 set `total` would be used. This allows using the progressbar
 with infinite, lazy, or slowly-calculated enumerators.

 @note
   If `total` is set, iteration will NOT stop after this number of
   iterations, only when provided Enumerable is finished. It may
   be convenient in "unsure number of iterations" situations
   (like downloading in chunks, when server may eventually send
   more chunks than predicted), but be careful to not pass infinite
   enumerators without previosly doing `.take(some_finite_number)`
   on them.

 @example
   bar.iterate(30.times) { ... }

 @param [Enumerable] collection
   the collection to iterate over

 @param [Integer] progress
   the amount to move progress bar by

 @return [Enumerator]

 @api public

Iterate over collection either yielding computation to block or provided Enumerator . If the bar s total was not set it would be taken from collection . count otherwise previously set total would be used . This allows using the progressbar with infinite lazy or slowly - calculated enumerators .


def update(options = {})
      synchronize do
        options.each do |name, val|
          if @configuration.respond_to?("#{name}=")
            @configuration.public_send("#{name}=", val)
          end
        end
      end
    end

def update ( options = { } ) synchronize do options . each do | name , val | if @configuration . respond_to? ( "#{name}=" ) @configuration . public_send ( "#{name}=" , val ) end end end end

Update configuration options for this bar

 @param [Hash[Symbol]] options
   the configuration options to update

 @api public


def render
      return if done?
      if hide_cursor && @last_render_width == 0 && !(@current >= total)
        write(TTY::Cursor.hide)
      end

      if @multibar
        characters_in = @multibar.line_inset(self)
        update(inset: self.class.display_columns(characters_in))
      end

      formatted = @formatter.decorate(self, @format)
      @tokens.each do |token, val|
        formatted = formatted.gsub(":#{token}", val)
      end

      padded = padout(formatted)

      write(padded, true)

      @last_render_time  = Time.now
      @last_render_width = self.class.display_columns(formatted)
    end

def render return if done? if hide_cursor && @last_render_width == 0 && ! ( @current >= total ) write ( TTY :: Cursor . hide ) end if @multibar characters_in = @multibar . line_inset ( self ) update ( inset : self . class . display_columns ( characters_in ) ) end formatted = @formatter . decorate ( self , @format ) @tokens . each do | token , val | formatted = formatted . gsub ( ":#{token}" , val ) end padded = padout ( formatted ) write ( padded , true ) @last_render_time = Time . now @last_render_width = self . class . display_columns ( formatted ) end

Render progress to the output

 @api private


def move_to_row
      if @multibar
        CURSOR_LOCK.synchronize do
          if @first_render
            @row = @multibar.next_row
            yield if block_given?
            output.print "\n"
            @first_render = false
          else
            lines_up = (@multibar.rows + 1) - @row
            output.print TTY::Cursor.save
            output.print TTY::Cursor.up(lines_up)
            yield if block_given?
            output.print TTY::Cursor.restore
          end
        end
      else
        yield if block_given?
      end
    end

def move_to_row if @multibar CURSOR_LOCK . synchronize do if @first_render @row = @multibar . next_row yield if block_given? output . print "\n" @first_render = false else lines_up = ( @multibar . rows + 1 ) - @row output . print TTY :: Cursor . save output . print TTY :: Cursor . up ( lines_up ) yield if block_given? output . print TTY :: Cursor . restore end end else yield if block_given? end end

Move cursor to a row of the current bar if the bar is rendered
 under a multibar. Otherwise, do not move and yield on current row.

 @api private

Move cursor to a row of the current bar if the bar is rendered under a multibar . Otherwise do not move and yield on current row .


def write(data, clear_first = false)
      return unless tty? # write only to terminal

      move_to_row do
        output.print(TTY::Cursor.column(1)) if clear_first
        characters_in = @multibar.line_inset(self) if @multibar
        output.print("#{characters_in}#{data}")
        output.flush
      end
    end

def
write
(
data
,
clear_first
=
false
)
return
unless
tty?
# write only to terminal
move_to_row
do
output
.
print
(
TTY
::
Cursor
.
column
(
1
)
)
if
clear_first
characters_in
=
@multibar
.
line_inset
(
self
)
if
@multibar
output
.
print
(
"#{characters_in}#{data}"
)
output
.
flush
end
end

Write out to the output

 @param [String] data

 @api private


def finish
      return if done?
      @current = total unless no_width
      render
      clear ? clear_line : write("\n", false)
    ensure
      @meter.clear
      @done = true

      # reenable cursor if it is turned off
      if hide_cursor && @last_render_width != 0
        write(TTY::Cursor.show, false)
      end

      emit(:done)
    end

def
finish
return
if
done?
@current
=
total
unless
no_width
render
clear
clear_line
:
write
(
"\n"
,
false
)
ensure
@meter
.
clear
@done
=
true
# reenable cursor if it is turned off
if
hide_cursor
&&
@last_render_width
!=
0
write
(
TTY
::
Cursor
.
show
,
false
)
end
emit
(
:done
)
end


def stop
      # reenable cursor if it is turned off
      if hide_cursor && @last_render_width != 0
        write(TTY::Cursor.show, false)
      end
      return if done?
      render
      clear ? clear_line : write("\n", false)
    ensure
      @meter.clear
      @stopped = true
      emit(:stopped)
    end

def
stop
# reenable cursor if it is turned off
if
hide_cursor
&&
@last_render_width
!=
0
write
(
TTY
::
Cursor
.
show
,
false
)
end
return
if
done?
render
clear
clear_line
:
write
(
"\n"
,
false
)
ensure
@meter
.
clear
@stopped
=
true
emit
(
:stopped
)
end

Stop and cancel the progress at the current position

 @api public


def log(message)
      sanitized_message = message.gsub(/\r|\n/, ' ')
      if done?
        write(sanitized_message + "\n", false)
        return
      end
      sanitized_message = padout(sanitized_message)

      write(sanitized_message + "\n", true)
      render
    end

def
log
(
message
)
sanitized_message
=
message
.
gsub
(
/
\r
\n
/
,
' '
)
if
done?
write
(
sanitized_message
+
"\n"
,
false
)
return
end
sanitized_message
=
padout
(
sanitized_message
)
write
(
sanitized_message
+
"\n"
,
true
)
render
end

Log message above the current progress bar

 @param [String] message
   the message to log out

 @api public


def padout(message)
      message_length = self.class.display_columns(message)

      if @last_render_width > message_length
        remaining_width = @last_render_width - message_length
        message += ' ' * remaining_width
      end
      message
    end

def
padout
(
message
)
message_length
=
self
.
class
.
display_columns
(
message
)
if
@last_render_width
>
message_length
remaining_width
=
@last_render_width
message_length
message
+=
' '
*
remaining_width
end
message
end

Pad message out with spaces

 @api private


def lock_exclusively!(max_run_time, worker = worker_name)
      now = self.class.db_time_now
      affected_rows = if locked_by != worker
        # We don't own this job so we will update the locked_by name and the locked_at
        self.class.update_all(["locked_at = ?, locked_by = ?", now, worker], ["id = ? and (locked_at is null or locked_at < ?)", id, (now - max_run_time.to_i)])
      else
        # We already own this job, this may happen if the job queue crashes.
        # Simply resume and update the locked_at
        self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker])
      end
      if affected_rows == 1
        self.locked_at    = now
        self.locked_by    = worker
        return true
      else
        return false
      end
    end

Lock this job for this worker.
 Returns true if we have the lock, false otherwise.

Lock this job for this worker . Returns true if we have the lock false otherwise .


def setup_tracery dir_path
      raise "Provided path not a directory" unless Dir.exist?(dir_path)

      @grammar = {}
      Dir.open(dir_path) do |dir|
        dir.each do |file|
          # skip our current and parent dir
          next if file =~ /^\.\.?$/

          # read the rule file into the files hash
          @grammar[file.split('.').first] =
            createGrammar(JSON.parse(File.read("#{dir_path}/#{file}")))
        end
      end

      # go ahead and makes a default mention-handler
      #  if we have a reply rule file
      unless @grammar['reply'].nil?
        on_reply { |bot|
          bot.reply_with_mentions('#default#', rules: 'reply')
        }
      end
    end

loads all of our tracery files into our +files+ hash
 if a file is named 'default' then we load that into +grammar+

 @param dir [String] path to the directory containing the tracery rules

loads all of our tracery files into our + files + hash if a file is named default then we load that into + grammar +


def expand_and_post(text, *options)
      opts = Hash[*options]
      rules = opts.fetch(:rules, 'default')
      actually_post(@grammar[rules].flatten(text),
                    **opts.reject {|k|
                      k == :rules
                    })
    end

def expand_and_post ( text , * options ) opts = Hash [ options ] rules = opts . fetch ( :rules , 'default' ) actually_post ( @grammar [ rules ] . flatten ( text ) , ** opts . reject { | k | k == :rules } ) end

a shortcut fuction for expanding text with tracery before posting

 @param text [String] the tracery text to expand before posting
 @param options [Hash] a hash of arguments to pass to post
 @option options rules [String] the grammar rules to load
 @option options visibility [String] visibility level
 @option options spoiler [String] text to use as content warning
 @option options reply_id [String] id of post to reply to
 @option options hide_media [Bool] should we hide media?
 @option options media [Array<String>] array of file paths


def run_interact
      @streamer.user do |update|
        if update.kind_of? Mastodon::Notification

          case update.type

          when 'mention'

            # this makes it so .content calls strip instead
            update.status.class.module_eval { alias_method :content, :strip } if @strip_html
            store_mention_data update.status
            @on_reply.call(self, update.status) unless @on_reply.nil?

          when 'reblog'
            @on_boost.call(self, update) unless @on_boost.nil?

          when 'favourite'
            @on_fave.call(self, update) unless @on_fave.nil?

          when 'follow'
            @on_follow.call(self, update) unless @on_follow.nil?

          end
        end
      end
    end

Starts a loop that checks for any notifications for the authenticated
 user, running the appropriate stored proc when needed

Starts a loop that checks for any notifications for the authenticated user running the appropriate stored proc when needed


def reply(text, *options)
      options = Hash[*options]

      post("@#{@mention_data[:account].acct} #{text}",
           **@mention_data.merge(options).reject { |k|
             k == :mentions or k == :account
           })
    end

Replies to the last mention the bot recieved using the mention's
   visibility and spoiler with +text+

 Automatically includes an @ for the account that mentioned the bot.
   Does not include any other @. See +reply_with_mentions+ if you want
   to automatically include all mentions

 @param text [String] text to post as a reply
 @param options [Hash] a hash of arguments to pass to post, overrides
   duplicating settings from last mention

Replies to the last mention the bot recieved using the mention s visibility and spoiler with + text +


def run_reply
      @streamer.user do |update|
        next unless update.kind_of? Mastodon::Notification and update.type == 'mention'

        # this makes it so .content calls strip instead
        update.status.class.module_eval { alias_method :content, :strip } if @strip_html

        store_mention_data update.status

        if block_given?
          yield(self, update.status)
        else
          @on_reply.call(self, update.status)
        end
      end
    end

Starts a loop that checks for mentions from the authenticated user account
 running a supplied block or, if a block is not provided, on_reply

Starts a loop that checks for mentions from the authenticated user account running a supplied block or if a block is not provided on_reply


def store_mention_data(mention)
      @mention_data = {
        reply_id: mention.id,
        visibility: mention.visibility,
        spoiler: mention.spoiler_text,
        hide_media: mention.sensitive?,
        mentions: mention.mentions,
        account: mention.account
      }
    end

def store_mention_data ( mention ) @mention_data = { reply_id : mention . id , visibility : mention . visibility , spoiler : mention . spoiler_text , hide_media : mention . sensitive? , mentions : mention . mentions , account : mention . account } end

Stores select data about a post into a hash for later use

 @param mention [Mastodon::Status] the most recent mention the bot received


def setup_streaming
      stream_uri = @client.instance()
                     .attributes['urls']['streaming_api'].gsub(/^wss?/, 'https')
      @streamer = Mastodon::Streaming::Client.new(base_url: stream_uri,
                                                  bearer_token: ENV['TOKEN'])
    end

def setup_streaming stream_uri = @client . instance ( ) . attributes [ 'urls' ] [ 'streaming_api' ] . gsub ( / / , 'https' ) @streamer = Mastodon :: Streaming :: Client . new ( base_url : stream_uri , bearer_token : ENV [ 'TOKEN' ] ) end


def parse_service_name(path)
      parts = Pathname.new(path).each_filename.to_a.reverse!
      # Find the last segment not in common segments, fall back to the last segment.
      parts.find {|seg| !COMMON_SEGMENTS[seg] } || parts.first
    end

Parse the service name from a path. Look at the last component of the
 path, ignoring some common names.

 @param path [String] Path to parse.
 @return [String]
 @example
   attribute(:service_name, kind_of: String, default: lazy { PoiseService::Utils.parse_service_name(path) })

Parse the service name from a path . Look at the last component of the path ignoring some common names .


def connect
      start_time = Time.now
      retries    = 0
      close

      # Number of times to try
      begin
        connect_to_server(servers, policy)
        logger.info(message: "Connected to #{address}", duration: (Time.now - start_time) * 1000) if respond_to?(:logger)
      rescue ConnectionFailure, ConnectionTimeout => exception
        cause = exception.is_a?(ConnectionTimeout) ? exception : exception.cause
        # Retry-able?
        if self.class.reconnect_on_errors.include?(cause.class) && (retries < connect_retry_count.to_i)
          retries += 1
          logger.warn "#connect Failed to connect to any of #{servers.join(',')}. Sleeping:#{connect_retry_interval}s. Retry: #{retries}" if respond_to?(:logger)
          sleep(connect_retry_interval)
          retry
        else
          message = "#connect Failed to connect to any of #{servers.join(',')} after #{retries} retries. #{exception.class}: #{exception.message}"
          logger.benchmark_error(message, exception: exception, duration: (Time.now - start_time)) if respond_to?(:logger)
          raise ConnectionFailure.new(message, address.to_s, cause)
        end
      end
    end

Create a new TCP Client connection

 Parameters:
   :server [String]
     URL of the server to connect to with port number
     'localhost:2000'
     '192.168.1.10:80'

   :servers [Array of String]
     Array of URL's of servers to connect to with port numbers
     ['server1:2000', 'server2:2000']

     The second server will only be attempted once the first server
     cannot be connected to or has timed out on connect
     A read failure or timeout will not result in switching to the second
     server, only a connection failure or during an automatic reconnect

   :connect_timeout [Float]
     Time in seconds to timeout when trying to connect to the server
     A value of -1 will cause the connect wait time to be infinite
     Default: 10 seconds

   :read_timeout [Float]
     Time in seconds to timeout on read
     Can be overridden by supplying a timeout in the read call
     Default: 60

   :write_timeout [Float]
     Time in seconds to timeout on write
     Can be overridden by supplying a timeout in the write call
     Default: 60

   :buffered [true|false]
     Whether to use Nagle's Buffering algorithm (http://en.wikipedia.org/wiki/Nagle's_algorithm)
     Recommend disabling for RPC style invocations where we don't want to wait for an
     ACK from the server before sending the last partial segment
     Buffering is recommended in a browser or file transfer style environment
     where multiple sends are expected during a single response.
     Also sets sync to true if buffered is false so that all data is sent immediately without
     internal buffering.
     Default: true

   :keepalive [true|false]
     Makes the OS check connections even when not in use, so that failed connections fail immediately
     upon use instead of possibly taking considerable time to fail.
     Default: true

   :connect_retry_count [Fixnum]
     Number of times to retry connecting when a connection fails
     Default: 10

   :connect_retry_interval [Float]
     Number of seconds between connection retry attempts after the first failed attempt
     Default: 0.5

   :retry_count [Fixnum]
     Number of times to retry when calling #retry_on_connection_failure
     This is independent of :connect_retry_count which still applies with
     connection failures. This retry controls upto how many times to retry the
     supplied block should a connection failure occur during the block
     Default: 3

   :on_connect [Proc]
     Directly after a connection is established and before it is made available
     for use this Block is invoked.
     Typical Use Cases:
     - Initialize per connection session sequence numbers.
     - Pass authentication information to the server.
     - Perform a handshake with the server.

   :policy [Symbol|Proc]
     Specify the policy to use when connecting to servers.
       :ordered
         Select a server in the order supplied in the array, with the first
         having the highest priority. The second server will only be connected
         to if the first server is unreachable
       :random
         Randomly select a server from the list every time a connection
         is established, including during automatic connection recovery.
       :ping_time
         FUTURE - Not implemented yet - Pull request anyone?
         The server with the lowest ping time will be tried first
       Proc:
         When a Proc is supplied, it will be called passing in the list
         of servers. The Proc must return one server name
           Example:
             :policy => Proc.new do |servers|
               servers.last
             end
       Default: :ordered

   :close_on_error [True|False]
     To prevent the connection from going into an inconsistent state
     automatically close the connection if an error occurs
     This includes a Read Timeout
     Default: true

   :proxy_server [String]
     The host name and port in the form of 'host_name:1234' to forward
     socket connections though.
     Default: nil ( none )

   SSL Options
   :ssl [true|false|Hash]
      true:  SSL is enabled using the SSL context defaults.
      false: SSL is not used.
      Hash:
        Keys from OpenSSL::SSL::SSLContext:
          ca_file, ca_path, cert, cert_store, ciphers, key, ssl_timeout, ssl_version
          verify_callback, verify_depth, verify_mode
        handshake_timeout: [Float]
          The number of seconds to timeout the SSL Handshake.
          Default: connect_timeout
      Default: false.
        See OpenSSL::SSL::SSLContext::DEFAULT_PARAMS for the defaults.

 Example:
   client = Net::TCPClient.new(
     server:                 'server:3300',
     connect_retry_interval: 0.1,
     connect_retry_count:    5
   )

   client.retry_on_connection_failure do
     client.write('Update the database')
   end

   # Read upto 20 characters from the server
   response = client.read(20)

   puts "Received: #{response}"
   client.close

 SSL Example:
   client = Net::TCPClient.new(
     server:                 'server:3300',
     connect_retry_interval: 0.1,
     connect_retry_count:    5,
     ssl:                    true
   )

 SSL with options Example:
   client = Net::TCPClient.new(
     server:                 'server:3300',
     connect_retry_interval: 0.1,
     connect_retry_count:    5,
     ssl:                    {
       verify_mode: OpenSSL::SSL::VERIFY_NONE
     }
   )
 Connect to the TCP server

 Raises Net::TCPClient::ConnectionTimeout when the time taken to create a connection
        exceeds the :connect_timeout
 Raises Net::TCPClient::ConnectionFailure whenever Socket raises an error such as Error::EACCESS etc, see Socket#connect for more information

 Error handling is implemented as follows:
 1. TCP Socket Connect failure:
    Cannot reach server
    Server is being restarted, or is not running
    Retry 50 times every 100ms before raising a Net::TCPClient::ConnectionFailure
    - Means all calls to #connect will take at least 5 seconds before failing if the server is not running
    - Allows hot restart of server process if it restarts within 5 seconds

 2. TCP Socket Connect timeout:
    Timed out after 5 seconds trying to connect to the server
    Usually means server is busy or the remote server disappeared off the network recently
    No retry, just raise a Net::TCPClient::ConnectionTimeout

 Note: When multiple servers are supplied it will only try to connect to
       the subsequent servers once the retry count has been exceeded

 Note: Calling #connect on an open connection will close the current connection
       and create a new connection


def write(data, timeout = write_timeout)
      data = data.to_s
      if respond_to?(:logger)
        payload        = {timeout: timeout}
        # With trace level also log the sent data
        payload[:data] = data if logger.trace?
        logger.benchmark_debug('#write', payload: payload) do
          payload[:bytes] = socket_write(data, timeout)
        end
      else
        socket_write(data, timeout)
      end
    rescue Exception => exc
      close if close_on_error
      raise exc
    end

Write data to the server

 Use #with_retry to add resilience to the #write method

 Raises Net::TCPClient::ConnectionFailure whenever the write fails
        For a description of the errors, see Socket#write

 Parameters
   timeout [Float]
     Optional: Override the default write timeout for this write
     Number of seconds before raising Net::TCPClient::WriteTimeout when no data has
     been written.
     A value of -1 will wait forever
     Default: :write_timeout supplied to #initialize

  Note: After a Net::TCPClient::ReadTimeout #read can be called again on
        the same socket to read the response later.
        If the application no longer wants the connection after a
        Net::TCPClient::ReadTimeout, then the #close method _must_ be called
        before calling _connect_ or _retry_on_connection_failure_ to create
        a new connection


def read(length, buffer = nil, timeout = read_timeout)
      if respond_to?(:logger)
        payload = {bytes: length, timeout: timeout}
        logger.benchmark_debug('#read', payload: payload) do
          data           = socket_read(length, buffer, timeout)
          # With trace level also log the received data
          payload[:data] = data if logger.trace?
          data
        end
      else
        socket_read(length, buffer, timeout)
      end
    rescue Exception => exc
      close if close_on_error
      raise exc
    end

Returns a response from the server

 Raises Net::TCPClient::ConnectionTimeout when the time taken to create a connection
        exceeds the :connect_timeout
        Connection is closed
 Raises Net::TCPClient::ConnectionFailure whenever Socket raises an error such as
        Error::EACCESS etc, see Socket#connect for more information
        Connection is closed
 Raises Net::TCPClient::ReadTimeout if the timeout has been exceeded waiting for the
        requested number of bytes from the server
        Partial data will not be returned
        Connection is _not_ closed and #read can be called again later
        to read the response from the connection

 Parameters
   length [Fixnum]
     The number of bytes to return
     #read will not return until 'length' bytes have been received from
     the server

   buffer [String]
    Optional buffer into which to write the data that is read.

   timeout [Float]
     Optional: Override the default read timeout for this read
     Number of seconds before raising Net::TCPClient::ReadTimeout when no data has
     been returned
     A value of -1 will wait forever for a response on the socket
     Default: :read_timeout supplied to #initialize

  Note: After a Net::TCPClient::ReadTimeout #read can be called again on
        the same socket to read the response later.
        If the application no longer wants the connection after a
        Net::TCPClient::ReadTimeout, then the #close method _must_ be called
        before calling _connect_ or _retry_on_connection_failure_ to create
        a new connection


def close
      socket.close if socket && !socket.closed?
      @socket  = nil
      @address = nil
      true
    rescue IOError => exception
      logger.warn "IOError when attempting to close socket: #{exception.class}: #{exception.message}" if respond_to?(:logger)
      false
    end

Close the socket only if it is not already closed

 Logs a warning if an error occurs trying to close the socket


def alive?
      return false if socket.nil? || closed?

      if IO.select([socket], nil, nil, 0)
        !socket.eof? rescue false
      else
        true
      end
    rescue IOError
      false
    end

def alive? return false if socket . nil? || closed? if IO . select ( [ socket ] , nil , nil , 0 ) ! socket . eof? rescue false else true end rescue IOError false end

Returns whether the connection to the server is alive

 It is useful to call this method before making a call to the server
 that would change data on the server

 Note: This method is only useful if the server closed the connection or
       if a previous connection failure occurred.
       If the server is hard killed this will still return true until one
       or more writes are attempted

 Note: In testing the overhead of this call is rather low, with the ability to
 make about 120,000 calls per second against an active connection.
 I.e. About 8.3 micro seconds per call


def socket_connect(socket, address, timeout)
      socket_address = Socket.pack_sockaddr_in(address.port, address.ip_address)

      # Timeout of -1 means wait forever for a connection
      return socket.connect(socket_address) if timeout == -1

      deadline = Time.now.utc + timeout
      begin
        non_blocking(socket, deadline) { socket.connect_nonblock(socket_address) }
      rescue Errno::EISCONN
        # Connection was successful.
      rescue NonBlockingTimeout
        raise ConnectionTimeout.new("Timed out after #{timeout} seconds trying to connect to #{address}")
      rescue SystemCallError, IOError => exception
        message = "#connect Connection failure connecting to '#{address.to_s}': #{exception.class}: #{exception.message}"
        logger.error message if respond_to?(:logger)
        raise ConnectionFailure.new(message, address.to_s, exception)
      end
    end

Connect to server

 Raises Net::TCPClient::ConnectionTimeout when the connection timeout has been exceeded
 Raises Net::TCPClient::ConnectionFailure


def socket_write(data, timeout)
      if timeout < 0
        socket.write(data)
      else
        deadline = Time.now.utc + timeout
        length = data.bytesize
        total_count = 0
        non_blocking(socket, deadline) do
          loop do
            begin
              count = socket.write_nonblock(data)
            rescue Errno::EWOULDBLOCK
              retry
            end
            total_count += count
            return total_count if total_count >= length
            data = data.byteslice(count..-1)
          end
        end
      end
    rescue NonBlockingTimeout
      logger.warn "#write Timeout after #{timeout} seconds" if respond_to?(:logger)
      raise WriteTimeout.new("Timed out after #{timeout} seconds trying to write to #{address}")
    rescue SystemCallError, IOError => exception
      message = "#write Connection failure while writing to '#{address.to_s}': #{exception.class}: #{exception.message}"
      logger.error message if respond_to?(:logger)
      raise ConnectionFailure.new(message, address.to_s, exception)
    end


def ssl_connect(socket, address, timeout)
      ssl_context = OpenSSL::SSL::SSLContext.new
      ssl_context.set_params(ssl.is_a?(Hash) ? ssl : {})

      ssl_socket            = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
      ssl_socket.hostname   = address.host_name
      ssl_socket.sync_close = true

      begin
        if timeout == -1
          # Timeout of -1 means wait forever for a connection
          ssl_socket.connect
        else
          deadline = Time.now.utc + timeout
          begin
            non_blocking(socket, deadline) { ssl_socket.connect_nonblock }
          rescue Errno::EISCONN
            # Connection was successful.
          rescue NonBlockingTimeout
            raise ConnectionTimeout.new("SSL handshake Timed out after #{timeout} seconds trying to connect to #{address.to_s}")
          end
        end
      rescue SystemCallError, OpenSSL::SSL::SSLError, IOError => exception
        message = "#connect SSL handshake failure with '#{address.to_s}': #{exception.class}: #{exception.message}"
        logger.error message if respond_to?(:logger)
        raise ConnectionFailure.new(message, address.to_s, exception)
      end

      # Verify Peer certificate
      ssl_verify(ssl_socket, address) if ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
      ssl_socket
    end

Try connecting to a single server
 Returns the connected socket

 Raises Net::TCPClient::ConnectionTimeout when the connection timeout has been exceeded
 Raises Net::TCPClient::ConnectionFailure


def party_mode new_master = nil
      return nil unless speakers.length > 1

      new_master = find_party_master if new_master.nil?

      party_over
      speakers.each do |slave|
        next if slave.uid == new_master.uid
        slave.join new_master
      end
      rescan @topology
    end

def party_mode new_master = nil return nil unless speakers . length > 1 new_master = find_party_master if new_master . nil? party_over speakers . each do | slave | next if slave . uid == new_master . uid slave . join new_master end rescan @topology end

Party Mode!  Join all speakers into a single group.

Party Mode! Join all speakers into a single group .


def discover
      result = SSDP::Consumer.new.search(service: 'urn:schemas-upnp-org:device:ZonePlayer:1', first_only: true, timeout: @timeout, filter: lambda {|r| r[:params]["ST"].match(/ZonePlayer/) })
      @first_device_ip = result[:address]
    end

Look for Sonos devices on the network and return the first IP address found
 @return [String] the IP address of the first Sonos device found


def topology
      self.discover unless @first_device_ip
      return [] unless @first_device_ip

      doc = Nokogiri::XML(open("http://#{@first_device_ip}:#{Sonos::PORT}/status/topology"))
      doc.xpath('//ZonePlayers/ZonePlayer').map do |node|
        TopologyNode.new(node)
      end
    end

Find all of the Sonos devices on the network
 @return [Array] an array of TopologyNode objects


def find(id)
      response = RestClient.get("#{@type.Resource}/#{id}")
      singular_resource = @type.Resource[0...-1]
      if response.body[singular_resource].nil?
        raise ArgumentError, 'Resource not found'
      end

      type.new.from_json(response.body[singular_resource].to_json)
    end

def
find
(
id
)
response
=
RestClient
.
get
(
"#{@type.Resource}/#{id}"
)
singular_resource
=
@type
.
Resource
[
0
...
1
]
if
response
.
body
[
singular_resource
]
.
nil?
raise
ArgumentError
,
'Resource not found'
end
type
.
new
.
from_json
(
response
.
body
[
singular_resource
]
.
to_json
)
end

Find a single resource by the resource id

 @param id [String] the resource id
 @return [Object] the Type object response


def all
      list = []
      page = 1
      fetch_all = true

      if @query.has_key?(:page)
        page = @query[:page]
        fetch_all = false
      end

      while true
        response = RestClient.get(@type.Resource, @query)
        data = response.body[@type.Resource]
        if !data.empty?
          data.each {|item| list << @type.new.from_json(item.to_json)}

          if !fetch_all
            break
          else
            where(page: page += 1)
          end
        else
          break
        end
      end

      return list
    end

def all list = [ ] page = 1 fetch_all = true if @query . has_key? ( :page ) page = @query [ :page ] fetch_all = false end while true response = RestClient . get ( @type . Resource , @query ) data = response . body [ @type . Resource ] if ! data . empty? data . each { | item | list << @type . new . from_json ( item . to_json ) } if ! fetch_all break else where ( page : page += 1 ) end else break end end return list end

Get all resources from a query by paging through data

 @return [Array<Object>] Array of resources


def validate(form)
      property = attributes.first

      # here is the thing: why does AM::UniquenessValidator require a filled-out record to work properly? also, why do we need to set
      # the class? it would be way easier to pass #validate a hash of attributes and get back an errors hash.
      # the class for the finder could either be infered from the record or set in the validator instance itself in the call to ::validates.
      record = form.model_for_property(property)
      record.send("#{property}=", form.send(property))

      @klass = record.class # this is usually done in the super-sucky #setup method.
      super(record).tap do |res|
        form.errors.add(property, record.errors.first.last) if record.errors.present?
      end
    end

when calling validates it should create the Vali instance already and set @klass there! # TODO: fix this in AM.


def validates(*args, &block)
      validation(name: :default, inherit: true) { validates *args, &block }
    end

def validates ( * args , & block ) validation ( name : :default , inherit : true ) { validates args , block } end

moved from reform as not applicable to dry


def update_xml(xml, value)
      wrap(xml).tap do |xml|
        if content?
          add(xml, value)
        elsif name?
          xml.name = value
        elsif array?
          value.each do |v|
            add(XML.add_node(xml, name), v)
          end
        else
          add(XML.add_node(xml, name), value)
        end
      end
    end

def update_xml ( xml , value ) wrap ( xml ) . tap do | xml | if content? add ( xml , value ) elsif name? xml . name = value elsif array? value . each do | v | add ( XML . add_node ( xml , name ) , v ) end else add ( XML . add_node ( xml , name ) , value ) end end end

Updates the text in the given _xml_ block to
 the _value_ provided.

Updates the text in the given _xml_ block to the _value_ provided .


def ipmt(rate, per, nper, pv, fv = 0, end_or_beginning = 0)
      pmt = self.pmt(rate, nper, pv, fv, end_or_beginning)
      fv = self.fv(rate, (per - 1), pmt, pv, end_or_beginning) * rate
      temp = end_or_beginning == 1 ? fv / (1 + rate) : fv

      (per == 1 && end_or_beginning == 1) ? 0.0 : temp
    end

def ipmt ( rate , per , nper , pv , fv = 0 , end_or_beginning = 0 ) pmt = self . pmt ( rate , nper , pv , fv , end_or_beginning ) fv = self . fv ( rate , ( per - 1 ) , pmt , pv , end_or_beginning ) * rate temp = end_or_beginning == 1 ? fv / ( 1 + rate ) : fv ( per == 1 && end_or_beginning == 1 ) ? 0.0 : temp end

Calculates the payment on interest for an investment based on
 constant-amount periodic payments and a constant interest rate.

 @param rate [Float] The interest rate as decimal (not per cent) per period
 @param per [Integer] The amortization period, in terms of number of periods
 @param nper [Integer] The number of payments to be made
 @param pv [Float] The present value
 @param fv [Float] The future value remaining after the final payment has been made
 @param end_or_begining [Integer] Whether payments are due at the end (0) or
   beggining (1) of each period

 @return [Float]

 @example
   Exonio.ipmt(0.075 / 12, 8, 12 * 2, 5000) # ==> -22.612926783996798

Calculates the payment on interest for an investment based on constant - amount periodic payments and a constant interest rate .


def nper(rate, pmt, pv, fv = 0, end_or_beginning = 0)
      z = pmt * (1 + rate * end_or_beginning) / rate
      temp = Math.log((-fv + z) / (pv + z))

      temp / Math.log(1 + rate)
    end

def nper ( rate , pmt , pv , fv = 0 , end_or_beginning = 0 ) z = pmt * ( 1 + rate * end_or_beginning ) / rate temp = Math . log ( ( - fv + z ) / ( pv + z ) ) temp / Math . log ( 1 + rate ) end

Calculates the number of payment periods for an investment based on
 constant-amount periodic payments and a constant interest rate.

 @param rate [Float] The interest rate as decimal (not per cent) per period
 @param pmt [Float] The payment amount made each period
 @param pv [Float] The present value of the payments
 @param fv [Float] The future value remaining after the final payment has been made
 @param end_or_begining [Integer] Whether payments are due at the end (0) or
   beggining (1) of each period

 @return [Float]

 @example
   Exonio.nper(0.07 / 12, -150, 8000) # ==> 64.07334877066185

Calculates the number of payment periods for an investment based on constant - amount periodic payments and a constant interest rate .


def pmt(rate, nper, pv, fv = 0, end_or_beginning = 0)
      temp = (1 + rate) ** nper
      fact = (1 + rate * end_or_beginning) * (temp - 1) / rate

      -(fv + pv * temp) / fact
    end

def pmt ( rate , nper , pv , fv = 0 , end_or_beginning = 0 ) temp = ( 1 + rate ) ** nper fact = ( 1 + rate * end_or_beginning ) * ( temp - 1 ) / rate - ( fv + pv * temp ) / fact end

Calculates the periodic payment for an annuity investment based on
 constant-amount periodic payments and a constant interest rate.

 @param rate [Float] The interest rate as decimal (not per cent) per period
 @param nper [Integer] The number of payments to be made (number of periods)
 @param pv [Float] The present value of the annuity
 @param fv [Float] The future value remaining after the final payment has been made
 @param end_or_begining [Integer] Whether payments are due at the end (0) or
   beggining (1) of each period

 @return [Float]

 @example
   Exonio.pmt(0.075/12, 12*15, 200_000) # ==> -1854.0247200054619

Calculates the periodic payment for an annuity investment based on constant - amount periodic payments and a constant interest rate .