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 ===============================================================================