Class: DYI::Length

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/dyi/length.rb

Overview

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).

  • As in CSS, the em and ex unit identifiers are relative to the current font’s font-size and x-height, respectively.

  • One px unit is defined to be equal to one user unit.

  • 1pt equals 1.25 user units.

  • 1pc equals 15 user units.

  • 1mm equals 3.543307 user units.

  • 1cm equals 35.43307 user units.

  • 1in equals 90 user units.

  • For percentage values that are defined to be relative to the size of parent element.

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

Constant Summary collapse

UNITS =

Array of unit that can be used.

Since:

  • 0.0.0

['px', 'pt', '%', 'cm', 'mm', 'in', 'em', 'ex', 'pc']
ZERO =

Zero length.

Since:

  • 0.0.0

new(0)
@@units =

Since:

  • 0.0.0

{'px'=>1.0,'pt'=>1.25,'cm'=>35.43307,'mm'=>3.543307,'in'=>90.0,'pc'=>15.0}
@@default_format =

Since:

  • 0.0.0

'0.###U'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(length) ⇒ Length #initialize(num) ⇒ Length #initialize(str) ⇒ Length

Returns a new instance of Length.

Overloads:

  • #initialize(length) ⇒ Length

    Returns the argument itself.

    Parameters:

    • length (Length)

      the source length

  • #initialize(num) ⇒ Length

    Parameters:

    • num (Numeric)

      the user unit value

  • #initialize(str) ⇒ Length

    Parameters:

    • str (String)

      the string that mean the length

Raises:

  • (ArgumentError)

    the argument is a string that could not be understand

  • (TypeError)

    the argument can not be coerced into Length

See Also:

Since:

  • 0.0.0



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/dyi/length.rb', line 85

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

Class Method Details

.default_formatString

Returns a format that is used when called #to_s without an argument.

Returns:

  • (String)

    a format string

See Also:

Since:

  • 0.0.0



471
472
473
# File 'lib/dyi/length.rb', line 471

def default_format
  @@default_format
end

.default_format=(fromat) ⇒ Object

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 Also:

Since:

  • 0.0.0



493
494
495
# File 'lib/dyi/length.rb', line 493

def default_format=(fromat)
  @@default_format = fromat.clone
end

.new(length) ⇒ Length .new(num) ⇒ Length .new(str) ⇒ Length

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.

Examples:

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

Overloads:

  • .new(length) ⇒ Length

    Returns the argument itself.

    Parameters:

    • length (Length)

      the source length

    Returns:

    • (Length)

      the argument itself

  • .new(num) ⇒ Length

    Parameters:

    • num (Numeric)

      the user unit value

  • .new(str) ⇒ Length

    Parameters:

    • str (String)

      the string that mean the length

Returns:

  • (Length)

    an instace of Length

Raises:

  • (ArgumentError)

    the argument is a string that could not be understand

  • (TypeError)

    the argument can not be coerced into Length

Since:

  • 0.0.0



409
410
411
412
# File 'lib/dyi/length.rb', line 409

def new(*args)
  return args.first if args.size == 1 && args.first.instance_of?(self)
  super
end

.new_or_nil(*args) ⇒ Length?

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

Returns:

  • (Length, nil)

    a new instace of Length if the argments is not nil, nil otherwise

See Also:

Since:

  • 0.0.0



419
420
421
# File 'lib/dyi/length.rb', line 419

def new_or_nil(*args)
  (args.size == 1 && args.first.nil?) ? nil : new(*args)
end

.set_default_format(format) { ... } ⇒ Coordinate .set_default_format(format) ⇒ String

Invokes block with given format string as default format.

Examples:

# 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"

Overloads:

  • .set_default_format(format) { ... } ⇒ Coordinate

    Invokes block with given format as default format. After invokes the block, the original format is used.

    Parameters:

    • format (String)

      a format string

    Yields:

    • a block which the format string is used in

    Returns:

  • .set_default_format(format) ⇒ String

    Sets default format setring as default_format= method.

    Parameters:

    • format (String)

      a format string

    Returns:

    • (String)

      the given argument

See Also:

Since:

  • 0.0.0



456
457
458
459
460
461
462
463
464
465
466
# File 'lib/dyi/length.rb', line 456

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) ⇒ Float

Returns a coefficient that is used for conversion from unit into user unit.

Parameters:

  • unit (String)

    a unit name

Returns:

  • (Float)

    a coefficient that is used for conversion

Raises:

  • (ArgumentError)

    a given unit can not be converted into another unit

Since:

  • 0.0.0



429
430
431
432
433
434
# File 'lib/dyi/length.rb', line 429

def unit_ratio(unit)
  unless ratio = @@units[unit.to_s]
    raise ArgumentError, "unit `#{unit}' can not be converted into another unit"
  end
  ratio
end

Instance Method Details

#%(other) ⇒ Number Also known as: modulo

Return a number which is the modulo after division of the receiver by other.

Parameters:

  • other (Length)

    the operand length

Returns:

  • (Number)

    a number which is the modul

Raises:

  • (TypeError)

    other can’t be coerced into Length

Since:

  • 0.0.0



185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/dyi/length.rb', line 185

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

#*(number) ⇒ Length

Returns a new muliplicative length of the receiver by number.

Parameters:

  • number (Numeric)

    the operand value

Returns:

  • (Length)

    a new muliplicative length

Since:

  • 0.0.0



152
153
154
# File 'lib/dyi/length.rb', line 152

def *(number)
  new_length(@value * number)
end

#**(number) ⇒ Length

Raises a length the number power.

Parameters:

  • number (Numeric)

    the operand value

Returns:

  • (Length)

    a length the number power

Since:

  • 0.0.0



159
160
161
# File 'lib/dyi/length.rb', line 159

def **(number)
  new_length(@value ** number)
end

#+(other) ⇒ Length

Returns a new length which is the sum of the receiver and other. First, other is converted into Length.

Parameters:

  • other (Length, Numeric, String)

    the value that can be converted into Length

Returns:

  • (Length)

    a new length which is the sum of the receiver and other

Since:

  • 0.0.0



125
126
127
128
129
130
131
132
# File 'lib/dyi/length.rb', line 125

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

#+@Length

Unary Plus – Returns the receiver’s value.

Returns:

  • (Length)

    receiver itself

Since:

  • 0.0.0



110
111
112
# File 'lib/dyi/length.rb', line 110

def +@
  self
end

#-(other) ⇒ Length

Returns a new length which is the difference of the receiver and other. First other is converted into Length.

Parameters:

  • other (Length, Numeric, String)

    the value that can be converted into Length

Returns:

  • (Length)

    a new length which is the difference of the receiver and other

Since:

  • 0.0.0



140
141
142
143
144
145
146
147
# File 'lib/dyi/length.rb', line 140

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

#-@Length

Unary Minus – Returns the receiver’s value, negated.

Returns:

  • (Length)

    the negated receiver’s value

Since:

  • 0.0.0



116
117
118
# File 'lib/dyi/length.rb', line 116

def -@
  new_length(-@value)
end

#/(other) ⇒ Float #/(number) ⇒ Length Also known as: quo

Divisional operator

Examples:

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

Overloads:

  • #/(other) ⇒ Float

    Returns a divisional float of the receiver by other.

    Parameters:

    • other (Length)

      the operand length

    Returns:

    • (Float)

      a divisional value

  • #/(number) ⇒ Length

    Returns a new divisional length of the receiver by number.

    Parameters:

    • other (Numeric)

      the operand value

    Returns:

    • (Length)

      a new divisional length

Raises:

  • (TypeError)

    the argument can’t be coerced into Length or Numeric

Since:

  • 0.0.0



211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/dyi/length.rb', line 211

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

#<=>(other) ⇒ Fixnum?

Comparision – compares two values. This is the basis for the tests in Comparable.

Returns:

  • (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.

Since:

  • 0.0.0



262
263
264
265
266
267
268
269
# File 'lib/dyi/length.rb', line 262

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

#absLength

Returns the absolute length of the receiver.

Returns:

  • (Length)

    the absolute length

Since:

  • 0.0.0



253
254
255
# File 'lib/dyi/length.rb', line 253

def abs
  @value >= 0 ? self : -self
end

#cloneObject

Raises:

  • (TypeError)

Since:

  • 0.0.0



230
231
232
# File 'lib/dyi/length.rb', line 230

def clone
  raise TypeError, "allocator undefined for Length"
end

#div(other) ⇒ Number

Returns a number which is the result of dividing the receiver by other.

Parameters:

  • other (Length)

    the operand length

Returns:

  • (Number)

    a number which is the result of dividing

Raises:

  • (TypeError)

    other can’t be coerced into Length

Since:

  • 0.0.0



167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/dyi/length.rb', line 167

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

#dupObject

Raises:

  • (TypeError)

Since:

  • 0.0.0



235
236
237
# File 'lib/dyi/length.rb', line 235

def dup
  raise TypeError, "allocator undefined for Length"
end

#inspectObject

Since:

  • 0.0.0



365
366
367
# File 'lib/dyi/length.rb', line 365

def inspect
  @value.to_s + @unit.to_s
end

#nonzero?Length?

Returns whether the receiver is a no-zero length.

Returns:

  • (Length, nil)

    self if the receiver is not zero, nil otherwise

Since:

  • 0.0.0



247
248
249
# File 'lib/dyi/length.rb', line 247

def nonzero?
  @value == 0 ? nil : self
end

#step(limit, step) {|len| ... } ⇒ Length #step(limit, step) ⇒ Enumrator

Invokes block with the sequence of length starting at receiver.

Overloads:

  • #step(limit, step) {|len| ... } ⇒ Length

    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).

    Parameters:

    • limit (Length)

      the limit of iteration continuation

    • step (Length)

      an iteration interval

    Yields:

    • (len)

      an iteration block

    Yield Parameters:

    • len (Length)

      the length that is created by stepping

    Returns:

    • (Length)

      the receiver itself

  • #step(limit, step) ⇒ Enumrator

    Returns an enumerator instead of the iteration.

    Parameters:

    • limit (Length)

      the limit of iteration continuation

    • step (Length)

      an iteration interval

    Returns:

    • (Enumrator)

      an enumrator for stepping

Since:

  • 0.0.0



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/dyi/length.rb', line 293

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) ⇒ Float

Returns amount part of the length converted into given unit as float. If parameter unit is given, converts into user unit.

Parameters:

  • unit (String) (defaults to: nil)

    a unit converted into

Returns:

  • (Float)

    amout part of the length

Raises:

  • (RuntimeError)

    length can not convert into other unit

  • (ArgumentError)

    unit is unknown unit

Since:

  • 0.0.0



350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/dyi/length.rb', line 350

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) ⇒ Length

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.

Examples:

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"

Parameters:

  • format (String) (defaults to: nil)

    a format string

Returns:

  • (Length)

    a string to represent the receiver

See Also:

Since:

  • 0.0.0



336
337
338
339
340
341
342
# File 'lib/dyi/length.rb', line 336

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_unitLength

Returns a length that converted into length of user unit.

Returns:

  • (Length)

    a length that converted into length of user unit

Since:

  • 0.0.0



314
315
316
# File 'lib/dyi/length.rb', line 314

def to_user_unit
  @unit ? self.class.new(to_f) : self
end

#unitString

Returns the receiver’s unit. If receiver has no unit, returns ‘px’.

Returns:

  • (String)

    the receiver’s unit

Since:

  • 0.0.0



273
274
275
# File 'lib/dyi/length.rb', line 273

def unit
  @unit.nil? ? 'px' : @unit
end

#zero?Boolean

Returns whether the receiver is a zero length.

Returns:

  • (Boolean)

    true if the receiver is a zero length, false otherwise

Since:

  • 0.0.0



241
242
243
# File 'lib/dyi/length.rb', line 241

def zero?
  @value == 0
end