On Sun, 28 Aug 2005, David Brady wrote:

> Okay, one more question from a C++ leopard trying to change his spots:
>
> I want to set up a program that uses some predefined values to determine its
> logic.  For example, a method could analyze some data and return "Good",
> "Fair", "Poor", or "Out of Bounds" based on a set of thresholds.  I want to
> be able to refer to these values by name in my code, so constants or symbols
> make a good choice here.  But I also want to build a sort of rule set around
> these values: some function returns values in the range of (0.0..1.0) and I
> want to be able to say that 0.8 is the minimum score for "Good", etc.  I
> also want the word "Good" stored in a specific single place so that I don't
> make any typos each time I need to print the description.
>
> In C++, I would use an enum for each of the values, then build arrays of
> floats and strings indexed by those enums to hold the thresholds and
> descriptions.
>
> What's the Ruby idiom for this?

you could use enums : it's easy enough to do clearly in ruby

   http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/6f37e998434e65e5/ea5a7cc77156ee36?q=ara+howard+enum&rnum=1#ea5a7cc77156ee36

> Perhaps Struct followed by some initializer arrays?  This seems like a good
> start but I end up wanting to build a set of constants first to use as keys
> to a hash containing the Structs.  Perhaps the Structs should be the
> constants themselves, like:
>
> Struct.new("RatingData", name, threshold, description)
>
> RATING_GOOD = Struct::RatingData( :Good, 0.8, "Good Rating" )
> RATING_FAIR = Struct::RatingData( :Fair, 0.5, "Fair Rating" )
>
> ...etc.  The analyze_data could return RATING_GOOD if things were fine.  So:
>
> rating = analyze_data data
> puts "Analysis: #{rating.description}"
>
> Though this doesn't let me treat the values as though they are ordered, e.g.

i often do something like this:

     harp:~ > cat a.rb
     class C
       module RATING
         LIST = [
           GOOD = 'GOOD',
           FAIR = 'FAIR',
           POOR = 'POOR',
         ]
       end
       RAITINGS = RATING::LIST
     end

     p C::RATING::GOOD
     p C::RAITINGS
     C::RAITINGS.each{|r| p r}


     harp:~ > ruby a.rb
     "GOOD"
     ["GOOD", "FAIR", "POOR"]
     "GOOD"
     "FAIR"
     "POOR"

remember - the main reason to use ints for enums in c/c++ is to be able to use
'==' with the variables but a language like ruby can do just as well with
strings.


> puts "*** Warning: Rating below Fair ***" if rating < RATING_FAIR
>
> Thoughts?

in any case, reading over your description i'd be inclined to wrap Raitings as
as objects and design a class generator that sets appropriate constants to
configurable raitings, something like:

     harp:~ > cat a.rb
     module RuleSet
       class Raiting
         include Comparable
         attr 'description'
         attr 'value'
         def initialize d, v
           @description, @value = String(d), Float(v)
         end
         def <=> other
           ov = other.value rescue other
           value <=> ov
         end
         def to_s
           "#{ description }(#{ value })"
         end
         alias inspect to_s
       end

       module ClassMehods
         attr 'raitings'
         def inspect; raitings.inspect; end
         def to_s; raitings.inspect; end
       end

       module InstanceMethods
       end

       class << self
         def new(spec = {'good' => 0.8, 'fair' => 0.5, 'poor' => 0.2,})
           klass = Class::new {
             include InstanceMethods
             extend ClassMehods
             @raitings = []
             %w( good fair poor ).each do |k|
               c = k.upcase
               ks = k.to_s
               ki = ks.intern
               keys = [k, ki, ks.downcase, ks.upcase]
               v = nil
               keys.each{|k| v = spec[k] and break}
               r = Raiting::new k, v
               const_set c, r
               @raitings << r
             end
           }
         end
         alias [] new
       end
     end

     rule_set = RuleSet::new
     p rule_set::raitings
     p(rule_set::GOOD < 42)
     p rule_set::GOOD.description
     p rule_set::GOOD.value


     rs =
       RuleSet[
         :good => 42,
         :fair => 41,
         :poor => 40,
       ]
     p rs
     p(rs::GOOD == 42)


     harp:~ > ruby a.rb
     [good(0.8), fair(0.5), poor(0.2)]
     true
     "good"
     0.8
     [good(42.0), fair(41.0), poor(40.0)]
     true


hth.

-a
-- 
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze.  --Nagarjuna
===============================================================================