class DYI::Length

This class representing a length. Length object holds an amount and a unit. The lists of unit identifiers matches the list of unit identifiers in CSS: em, ex, px, pt, pc, cm, mm, in and percentages(%). When a unit is not given, then the length is assumed to be in user units (i.e., a value in the current user coordinate sytem).

Distuptive Change

Length is not possible to change destructively. #clone method and #dup method raise TypeError.

Ways of Comparing and Calculating

This class includes Comparable module, therefore you can use the comparative operators. In the comparison between DYI::Length objects, the unit of each objects are arranged and it compares. The equality operator ‘==’ does not test equality instance but test equality value.

DYI::Length.new('1in') > DYI::Length.new(50)        # => true, 1in equals 90px.
DYI::Length.new('1in') > DYI::Length.new('2em')     # => Error, 'em' is not comparable unit.
DYI::Length.new('10cm') == DYI::Length.new('100mm') # => true

This class suports following arithmetic operators and methods: {#+ +}, {#- -}, {#* *}, {#/ /}, {#% %}, {#** **}, {#div}, {#quo}, {#modulo}. The operators ‘{#+ +}’, ‘{#- -}’ coerces a right hand operand into Length, and then calculates.

@since 0.0.0

Constants

UNITS

Array of unit that can be used.

ZERO

Zero length.

Public Class Methods

default_format() click to toggle source

Returns a format that is used when called {#to_s} without an argument. @return [String] a format string @see .default_format=

# File lib/dyi/length.rb, line 470
def default_format
  @@default_format
end
default_format=(fromat) click to toggle source

Sets a format string that is used when called {#to_s}. The format string that is set at this method is used permanently. Use {.set_default_format} with a block when you want to use a format string temporarily.

The format string is same as {Numeric#strfnum} format. In addition to place-holder of {Numeric#strfnum}, following placeholder can be used.

"u" (unit placeholder)

Placeholder ‘u’ is replaced as a unit. If the unit is user unit, ‘u’ is repleced as ‘px’.

"U" (unit placeholder)

Placeholder ‘U’ is replaced as a unit. If the unit is user unit, ‘U’ is replece as empty string.

"\" (Escape Character)

Causes the next character to be interpreted as a literal.

@see to_s @see .set_default_format @see Numeric#strfnum

# File lib/dyi/length.rb, line 492
def default_format=(fromat)
  @@default_format = fromat.clone
end
new(length) click to toggle source

@overload initialize(length)

Returns the argument itself.
@param [Length] length the source length

@overload initialize(num)

@param [Numeric] num the user unit value

@overload initialize(str)

@param [String] str the string that mean the length

@raise [ArgumentError] the argument is a string that could not be understand @raise [TypeError] the argument can not be coerced into Length @see .new

# File lib/dyi/length.rb, line 84
def initialize(length)
  case length
  when Length
    @value = length._num
    @unit = length._unit
  when Numeric
    @value = length
    @unit = nil
  when String
    unless /^\s*(-?[\d]*(?:\d\.|\.\d|\d)[0\d]*)(#{UNITS.join('|')})?\s*$/ =~ length
      raise ArgumentError, "`#{length}' is string that could not be understand"
    end
    __value, __unit = $1, $2
    @value = __value.include?('.') ? __value.to_f : __value.to_i
    @unit = (__unit == 'px' || @value == 0) ? nil : __unit
  else
    raise TypeError, "#{length.class} can't be coerced into Length"
  end
end
new(*args) click to toggle source

Creates and returns a new instance of Length provided the argument is not an instace of Length. If the argument is an instace of Length, returns the argument itself. @overload new(length)

Returns the argument itself.
@param [Length] length the source length
@return [Length] the argument itself

@overload new(num)

@param [Numeric] num the user unit value

@overload new(str)

@param [String] str the string that mean the length

@return [Length] an instace of Length @raise (see initialize) @example

length1 = DYI::Length.new(10)      # 10 user unit (equals to 10px)
length2 = DYI::Length.new('10')    # it is 10px too
length3 = DYI::Length.new('10px')  # it is 10px too
Calls superclass method
# File lib/dyi/length.rb, line 408
def new(*args)
  return args.first if args.size == 1 && args.first.instance_of?(self)
  super
end
new_or_nil(*args) click to toggle source

Returns a new instace of Length if the argments is not nil (calls Length.new method), but returns nil if the argument is nil. @return [Length, nil] a new instace of Length if the argments is not

nil, nil otherwise

@see .new

# File lib/dyi/length.rb, line 418
def new_or_nil(*args)
  (args.size == 1 && args.first.nil?) ? nil : new(*args)
end
set_default_format(format) { || ... } click to toggle source

Invokes block with given format string as default format. @overload set_default_format(format)

Invokes block with given _format_ as default format. After invokes the
block, the original format is used.
@param [String] format a format string
@yield a block which the format string is used in
@return [Coordinate] the receiver itself

@overload set_default_format(format)

Sets default format setring as {.default_format=} method.
@param [String] format a format string
@return [String] the given argument

@example

# an initial format string is "#.###U"
len = DYI::Length.new(1234.56)
len.to_s                      # => "1234.56"
DYI::Length.set_default_format('#,##0u') {
  len.to_s                    # => "1,235px"
}
len.to_s                      # => "1234.56"

@see .default_format=

# File lib/dyi/length.rb, line 455
def set_default_format(format)
  if block_given?
    org_format = @@default_format
    self.default_format = format
    yield
    @@default_format = org_format
    self
  else
    self.default_format = format
  end
end
unit_ratio(unit) click to toggle source

Returns a coefficient that is used for conversion from unit into user unit. @param [String] unit a unit name @return [Float] a coefficient that is used for conversion @raise [ArgumentError] a given unit can not be converted into another

unit
# File lib/dyi/length.rb, line 428
def unit_ratio(unit)
  unless ratio = @@units[unit.to_s]
    raise ArgumentError, "unit `#{unit}' can not be converted into another unit"
  end
  ratio
end

Public Instance Methods

%(other) click to toggle source

Return a number which is the modulo after division of the receiver by other. @param [Length] other the operand length @return [Number] a number which is the modul @raise [TypeError] other can’t be coerced into Length

# File lib/dyi/length.rb, line 184
def % (other)
  case other
  when Length
    if @unit == other.unit
      new_length(@value % other._num)
    else
      self.class.new(to_f % other.to_f)
    end
  else
    raise TypeError, "#{other.class} can't be coerced into Length"
  end
end
Also aliased as: modulo
*(number) click to toggle source

Returns a new muliplicative length of the receiver by number. @param [Numeric] number the operand value @return [Length] a new muliplicative length

# File lib/dyi/length.rb, line 151
def *(number)
  new_length(@value * number)
end
**(number) click to toggle source

Raises a length the number power. @param [Numeric] number the operand value @return [Length] a length the number power

# File lib/dyi/length.rb, line 158
def **(number)
  new_length(@value ** number)
end
+(other) click to toggle source

Returns a new length which is the sum of the receiver and other. First, other is converted into Length. @param [Length, Numeric, String] other the value that can be converted

into +Length+

@return [Length] a new length which is the sum of the receiver and other

# File lib/dyi/length.rb, line 124
def +(other)
  other = self.class.new(other)
  if @unit == other._unit
    new_length(@value + other._num)
  else
    self.class.new(to_f + other.to_f)
  end
end
+@() click to toggle source

Unary Plus – Returns the receiver’s value. @return [Length] receiver itself

# File lib/dyi/length.rb, line 109
def +@
  self
end
-(other) click to toggle source

Returns a new length which is the difference of the receiver and other. First other is converted into Length. @param [Length, Numeric, String] other the value that can be converted

into +Length+

@return [Length] a new length which is the difference of the receiver and

_other_
# File lib/dyi/length.rb, line 139
def -(other)
  other = self.class.new(other)
  if @unit == other._unit
    new_length(@value - other._num)
  else
    self.class.new(to_f - other.to_f)
  end
end
-@() click to toggle source

Unary Minus – Returns the receiver’s value, negated. @return [Length] the negated receiver’s value

# File lib/dyi/length.rb, line 115
def -@
  new_length(-@value)
end
/(number) click to toggle source

Divisional operator @overload /(other)

Returns a divisional float of the receiver by _other_.
@param [Length] other the operand length
@return [Float] a divisional value

@overload /(number)

Returns a new divisional length of the receiver by _number_.
@param [Numeric] other the operand value
@return [Length] a new divisional length

@example

DYI::Length.new(10) / 4                  # => 2.5px
DYI::Length.new(10) / DYI::Length.new(4) # => 2.5

@raise [TypeError] the argument can’t be coerced into Length or Numeric

# File lib/dyi/length.rb, line 210
def /(number)
  case number
  when Numeric
    new_length(@value.quo(number.to_f))
  when Length
    if @unit == number.unit
      @value.quo(number._num.to_f)
    else
      to_f.quo(number.to_f)
    end
  else
    raise TypeError, "#{number.class} can't be coerced into Numeric or Length"
  end
end
Also aliased as: quo
<=>(other) click to toggle source

Comparision – compares two values. This is the basis for the tests in Comparable. @return [Fixnum, nil] -1, 0, +1 or nil depending on whether

the receiver is less than, equal to, greater than _other_; if is not
comparable, +nil+.
# File lib/dyi/length.rb, line 261
def <=>(other)
  return nil unless other.kind_of?(Length)
  if @unit == other._unit
    @value <=> other._num
  else
    to_f <=> other.to_f rescue nil
  end
end
abs() click to toggle source

Returns the absolute length of the receiver. @return [Length] the absolute length

# File lib/dyi/length.rb, line 252
def abs
  @value >= 0 ? self : -self
end
clone() click to toggle source

@private

# File lib/dyi/length.rb, line 229
def clone
  raise TypeError, "allocator undefined for Length"
end
div(other) click to toggle source

Returns a number which is the result of dividing the receiver by other. @param [Length] other the operand length @return [Number] a number which is the result of dividing @raise [TypeError] other can’t be coerced into Length

# File lib/dyi/length.rb, line 166
def div(other)
  case other
  when Length
    if @unit == other.unit
      @value.div(other._num)
    else
      to_f.div(other.to_f)
    end
  else
    raise TypeError, "#{other.class} can't be coerced into Length"
  end
end
dup() click to toggle source

@private

# File lib/dyi/length.rb, line 234
def dup
  raise TypeError, "allocator undefined for Length"
end
inspect() click to toggle source

@private

# File lib/dyi/length.rb, line 364
def inspect
  @value.to_s + @unit.to_s
end
modulo(other)
Alias for: %
nonzero?() click to toggle source

Returns whether the receiver is a no-zero length. @return [Length, nil] self if the receiver is not zero, nil otherwise

# File lib/dyi/length.rb, line 246
def nonzero?
  @value == 0 ? nil : self
end
quo(number)
Alias for: /
step(limit, step) click to toggle source

Invokes block with the sequence of length starting at receiver. @overload step (limit, step)

Invokes block with the sequence of length starting at receiver,
incremented by _step_ on each call. The loop finishes when _length_ to
be passed to the block is greater than _limit_ (if _step_ is positive)
or less than _limit_ (if _step_ is negative).
@param [Length] limit the limit of iteration continuation
@param [Length] step an iteration interval
@yield [len] an iteration block
@yieldparam [Length] len the length that is created by stepping
@return [Length] the receiver itself

@overload step (limit, step)

Returns an enumerator instead of the iteration.
@param [Length] limit the limit of iteration continuation
@param [Length] step an iteration interval
@return [Enumrator] an enumrator for stepping
# File lib/dyi/length.rb, line 292
def step(limit, step)
  if @unit == limit._unit && @unit == step._unit
    self_value, limit_value, step_value = @value, limit._num, step._num
  else
    self_value, limit_value, step_value = to_f, limit.to_f, step.to_f
  end
  enum = Enumerator.new {|y|
    self_value.step(limit_value, step_value) do |value|
      self.new_length(value)
    end
  }
  if block_given?
    enum.each(&proc)
    self
  else
    enum
  end
end
to_f(unit=nil) click to toggle source

Returns amount part of the length converted into given unit as float. If parameter unit is given, converts into user unit. @param [String] unit a unit converted into @return [Float] amout part of the length @raise [RuntimeError] length can not convert into other unit @raise [ArgumentError] unit is unknown unit

# File lib/dyi/length.rb, line 349
def to_f(unit=nil)
  unless self_ratio = @unit ? @@units[@unit] : 1.0
    raise RuntimeError, "unit `#{@unit}' can not convert into user unit"
  end
  unless param_ratio = unit ? @@units[unit] : 1.0
    if UNITS.include?(unit)
      raise RuntimeError, "unit `#{@unit}' can not convert into user unit"
    else
      raise ArgumentError, "unit `#{@unit}' is unknown unit"
    end
  end
  (@value * self_ratio.quo(param_ratio)).to_f
end
to_s(format=nil) click to toggle source

Returns a string to represent the receiver.

Format string can be specified for the argument. If no argument is given, {.default_format} is used as format string. About format string, see the documentation of {.default_format} method. @param [String] format a format string @return [Length] a string to represent the receiver @example

len1 = DYI::Length.new(1234.56)
len1.to_s('0.#u')     # => "1234.6px"
len1.to_s('0.#U')     # => "1234.6"
len1.to_s('#,#0U')    # => "1,235"

len2 = DYI::Length.new('10pt')
len2.to_s('0u')       # => "10pt"
len2.to_s('0U')       # => "10pt"

@see .default_format= @see .set_default_format

# File lib/dyi/length.rb, line 335
def to_s(format=nil)
  fmts = (format || @@default_format).split('\\\\')
  fmts = fmts.map do |fmt|
    fmt.gsub(/(?!\\U)(.|\G)U/, '\\1' + (@unit == '%' ? '\\%' : @unit.to_s)).gsub(/(?!\\u)(.|\G)u/, '\\1' + (@unit == '%' ? '\\%' : unit))
  end
  @value.strfnum(fmts.join('\\\\'))
end
to_user_unit() click to toggle source

Returns a length that converted into length of user unit. @return [Length] a length that converted into length of user unit

# File lib/dyi/length.rb, line 313
def to_user_unit
  @unit ? self.class.new(to_f) : self
end
unit() click to toggle source

Returns the receiver’s unit. If receiver has no unit, returns ‘px’. @return [String] the receiver’s unit

# File lib/dyi/length.rb, line 272
def unit
  @unit.nil? ? 'px' : @unit
end
zero?() click to toggle source

Returns whether the receiver is a zero length. @return [Boolean] true if the receiver is a zero length, false otherwise

# File lib/dyi/length.rb, line 240
def zero?
  @value == 0
end

Protected Instance Methods

_num() click to toggle source
# File lib/dyi/length.rb, line 370
def _num
  @value
end
_unit() click to toggle source
# File lib/dyi/length.rb, line 374
def _unit
  @unit
end

Private Instance Methods

new_length(value) click to toggle source
# File lib/dyi/length.rb, line 380
def new_length(value)
  other = self.class.allocate
  other.instance_variable_set(:@value, value)
  other.instance_variable_set(:@unit, @unit)
  other
end