Class: GreaterLess

Inherits:
Object
  • Object
show all
Defined in:
lib/greater_less.rb

Overview

The GreaterLess class can be used to generate objects that represent halfopen intervals, but transparently behave as Floats. One easy way to integrate this class into your project is by requiring the greater_less string extension as follows:

require 'greater_less/string_extension'

This extension redifines the #to_f method of the String class as follows:

class String
  alias :to_f_without_greater_less :to_f

  def to_f
    if self =~ GreaterLess::GREATER_LESS
      return GreaterLess.new(self)
    end
    self.to_f_without_greater_less
  end
end

Now when a string starts with a greater or less sign (like for instance "> 3.45"), the #to_f method converts it to a GreaterLess object instead of the value 0.0.

With this extension in place one can simply convert strings like the one above to a float like object and compare it to floats as if it were a float itself. For instance one can do the following:

>> value = ">3.45".to_f
=> > 3.45
>> value > 2.45
=> true
>> value >= 2.45
=> true
>> 2.45 > value
=> false
>> 2.45 >= value
=> false
>> value == ">3.45".to_f
=> true
>> value != 2.45
=> true

It is also possible to compare GreaterLess values with each other, so you do not have to worry about what kind of object you are dealing with in your code:

>> value1 = ">3.45".to_f
=> > 3.45
>> value2 = "< 2.45".to_f
=> < 2.45
>> value1 > value2
=> true
>> value2 > value1
=> false

Finally it is possible to apply simple arithmetics to GreaterLess objects like addition, subtraction, multiplication and division:

>> value = ">3.45".to_f
=> > 3.45
>> value + 2
=> > 5.45
>> value - 2
=> > 1.4500000000000002
>> value * 2
=> > 1.725

Inverting the object’s sign when multiplying with a negative numerical or using a GreaterLess object in the denominator is nicely dealt with:

>> value = ">3.45".to_f
=> > 3.45
>> -1 * value
=> < -3.45
>> 1 / value
=> < 0.2898550724637681
>> -1 / value
=> > -0.2898550724637681

It makes no sense to apply the operators +, -, * or / on a pair of GreaterLess objects, so an exception is raised in these cases.

All other methods are simply passed to the float value the GreaterLess object contains, so that it transparently acts like a float.

Constant Summary collapse

GREATER_LESS =
/^[<>] ?/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(content) ⇒ GreaterLess

Returns a new instance of GreaterLess.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/greater_less.rb', line 109

def initialize(content)
  if content.is_a? String
    if content =~ /^>/
      @sign = ">"
    elsif content =~ /^</
      @sign = "<"
    end
    @float = content.gsub(/^[<>] ?/, "").to_f
  elsif content.is_a? Numeric
    @float = content.to_f
  else
    raise "Can't handle #{content.class}!"
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(*args) ⇒ Object



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

def method_missing(*args)
  @float.send(*args)
end

Class Method Details

.new(content, coerce = false) ⇒ Object



100
101
102
103
104
105
106
# File 'lib/greater_less.rb', line 100

def new(content, coerce=false)
  if coerce or content =~ GREATER_LESS
    old_new(content)
  else
    content.to_f
  end
end

.old_newObject



98
# File 'lib/greater_less.rb', line 98

alias :old_new :new

Instance Method Details

#!=(numerical) ⇒ Object



170
171
172
# File 'lib/greater_less.rb', line 170

def !=(numerical)
  not self == numerical
end

#*(numerical) ⇒ Object



182
183
184
185
186
187
188
189
190
# File 'lib/greater_less.rb', line 182

def *(numerical)
  value, sign = if numerical.is_a? self.class
    raise "Can't handle #{self.class}!" if @sign
    [@float * numerical.value, @float > 0 ? numerical.sign : numerical.inverted_sign]
  else
    [@float * numerical, numerical > 0 ? @sign : inverted_sign]
  end
  GreaterLess.new("#{sign} #{value}")
end

#+(numerical) ⇒ Object



202
203
204
205
206
207
208
209
210
# File 'lib/greater_less.rb', line 202

def +(numerical)
  value, sign = if numerical.is_a? self.class
    raise "Can't handle #{self.class}!" if @sign
    [@float + numerical.value, numerical.sign]
  else
    [@float + numerical, @sign]
  end
  GreaterLess.new("#{sign} #{value}")
end

#-(numerical) ⇒ Object



212
213
214
# File 'lib/greater_less.rb', line 212

def -(numerical)
  self + -numerical
end

#-@Object



216
217
218
# File 'lib/greater_less.rb', line 216

def -@
  GreaterLess.new("#{inverted_sign} -#{value}")
end

#/(numerical) ⇒ Object



192
193
194
195
196
197
198
199
200
# File 'lib/greater_less.rb', line 192

def /(numerical)
  value, sign = if numerical.is_a? self.class
    raise "Can't handle #{self.class}!" if @sign
    [@float / numerical.value, @float > 0 ? numerical.inverted_sign : numerical.sign]
  else
    [@float / numerical, numerical > 0 ? @sign : inverted_sign]
  end
  GreaterLess.new("#{sign} #{value}")
end

#<(numerical) ⇒ Object



166
167
168
# File 'lib/greater_less.rb', line 166

def <(numerical)
  numerical > self
end

#<=(numerical) ⇒ Object



178
179
180
# File 'lib/greater_less.rb', line 178

def <=(numerical)
  self == numerical or self < numerical
end

#==(numerical) ⇒ Object

:doc:



150
151
152
153
154
155
156
# File 'lib/greater_less.rb', line 150

def ==(numerical)
  if numerical.is_a? self.class
    @sign == numerical.sign and @float == numerical.value
  else
    false
  end
end

#>(numerical) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/greater_less.rb', line 158

def >(numerical)
  if numerical.is_a? self.class
    @float >= numerical.value and [nil, ">"].include? @sign and [nil, "<"].include? numerical.sign
  else
    @float >= numerical and @sign == ">"
  end
end

#>=(numerical) ⇒ Object



174
175
176
# File 'lib/greater_less.rb', line 174

def >=(numerical)
  self == numerical or self > numerical
end

#coerce(object) ⇒ Object



124
125
126
127
128
129
130
# File 'lib/greater_less.rb', line 124

def coerce(object)
  if object.is_a? Numeric and not object.is_a? self.class
    [GreaterLess.new(object, true), self]
  else
    raise "Can't handle #{object.class}!"
  end
end

#inspectObject



229
230
231
# File 'lib/greater_less.rb', line 229

def inspect
  self.to_s
end

#inverted_signObject



136
137
138
139
140
141
142
143
# File 'lib/greater_less.rb', line 136

def inverted_sign
  case @sign
  when ">"
    "<"
  when "<"
    ">"
  end
end

#is_a?(klass) ⇒ Boolean

Returns:

  • (Boolean)


233
234
235
236
237
238
239
# File 'lib/greater_less.rb', line 233

def is_a?(klass)
  if klass == self.class
    true
  else
    @float.is_a? klass
  end
end

#signObject



132
133
134
# File 'lib/greater_less.rb', line 132

def sign
  @sign
end

#to_fObject

:nodoc:



221
222
223
# File 'lib/greater_less.rb', line 221

def to_f
  self
end

#to_sObject



225
226
227
# File 'lib/greater_less.rb', line 225

def to_s
  "#{@sign} #{@float}"
end

#valueObject



145
146
147
# File 'lib/greater_less.rb', line 145

def value
  @float
end