Friday, November 18, 2011

named_value_class 0.6.0 released

http://rubygems.org/gems/named_value_class
http://github.com/mgarriss/named_value_class

Quickly add customizable class delegate constants which output their names, not their values. This may be desirable for some DSLs.

Features

  • Creates a class for constants that delegate most work to the stable and efficient ruby core classes.
  • #to_s and #inspect output is the constant’s name and not it’s value.
  • Output is eval’able.
  • Allows for lowercase constant names.
  • Provides a module for each new class to include all defined values into some other namespace.
  • Provides a mapping from value back to constant.
  • Only dependancy is the wonderful ‘delegate’ library which is already in the ruby stdlib so… really no dependancies.

Installation

gem install named_value_class

Requirements

  • Ruby 1.9.2 (named arguments, and maintained insertion order hashes)

Usage

require "named_value_class"
Create a new class named MathFloat that delegates to Float and one named Note that delegates to Fixnum. The Note class has an instance method added called #play_midi and redefines how substraction works. Create two MathFloat constants and one Note constant.
module MyApp
  NamedValueClass MathFloat:Float
  MathFloat Pi: 3.14159265358979323846
  MathFloat Phi:1.61803398874989
  
  NamedValueClass Note:Fixnum do
    # this is eval'd in Note's class scope

    attr_reader :some_var

    def play_midi
      # do something interesting here
    end
    
    # maybe define some special cases for subtraction
    alias _minus :-
    def -(rhs)
      if rhs.is_a? String
        "you jammin mon!"
      else
        self._minus(rhs)
      end
    end
  end
  Note A4:440 # Hertz
  Note A5:880 # Hertz
end
Access the constants.
MyApp::MathFloat::Pi + MyApp::MathFloat::Phi

# or

include MyApp::MathFloat::NamedValues
Pi + Phi
All constants created in the above way can be included anywhere using the NamedValues module defined within the new class created by NamedValueClass().
The following output the constant’s name and not the underlying value.
Pi                #=> Pi
Phi               #=> Phi
Treat the defined value as the type they delegate to. The following output a value because the results are regular Floats.
Pi / 2            #=> 1.5707963267948966
Phi / 2           #=> 0.809016994374945
Pi + Phi          #=> 4.759626642339683
Use them on left hand or right hand side.
/ Pi            #=> 0.954929658551372
/ Phi           #=> 1.2360679774997934
Just showing constant names here, note that this output is eval’able.
[Pi,Phi]          #=> [Pi,Phi]
We can still get the original inspect and to_s methods.
Phi.value_inspect #=> "1.61803398874989"
Phi.value_to_s    #=> "1.61803398874989"
Get access to an Array of all created constants like so.
MyApp::Note::NamedValues::Collection  #=> [A4]
Call added instance methods.
MyApp::Note::A4.play_midi
Everything works at the global scope.
NamedValueClass Foobar:Array
Foobar B1:[1,2]
Additional attribues can be added to any instance.
Foobar B2:[],    some_param:'dude2'foo:5, bar:89
Foobar B3:[2,1], foo:99
Foobar B4:[2,1], bar:9.3
All instances will have setters and getters for any named parameter given.
Foobar::B2.some_param                    #=> 'dude2'
Foobar::B2.some_param = 'kind of cool'
Foobar::B2.some_param                    #=> 'kind of cool'
Use lowercase names. Before you complain, please consider the standard abbreviations for the Diatonic Interval names in music theory. ‘m’ and ‘M’ have distinct meanings:
module Music
  NamedValueClass Interval:Fixnum

  Interval P1: 0   # value is number of semitones
  Interval m2: 1
  Interval M2: 2
  Interval m3: 3
  Interval M3: 4
  Interval P4: 5
  Interval A4: 6
  Interval d5: 6
  Interval P5: 7
  Interval m6: 8
  Interval M6: 9
  Interval m7:10
  Interval M7:11
  Interval P8:12
  
  class Chromatic < Interval; end
  Chromatic d3:2
end

class Scale
  include Music::Interval::NamedValues

  MinorScale = [P1,M2,m3,P4,P5,m6,m7]
  DorianMode = [P1,M2,m3,P4,P5,M6,m7]
end
A mapping from values back to named values is provided using [].
Music::Interval[3] #=> m3
Supports inhertance like so:
module Music
  class Chromatic < Interval; end
  
  Chromatic d3:2
end
You get the idea. Your homework: Scale should be a NamedValueClass. Scale::A and Scale::Am, etc.

Thoughts

I’m still not settled on a best practice for coercion, so it remains missing from the helpers and specs. You can of course just write a coerce method for any named value class you create.
This gem was created out of a refactor of my music theroy calculator gem, Peas. It greatly reduced the code clutter of that gem. I’d say it reduced the number lines by half at least.

Issues

  • If you run into this error: “super from singleton method that is defined to multiple classes is not supported; this will be fixed in 1.9.3 or later” you should know that ruby is producing this message, not me. Try declaring instances directly after the NamedValueClass call for each new type. I’m still trying to accurately repoduce this problem in specs and a solution evades me currently. This is crazy annoying and makes the gem far less useful at the moment. I delclare that it will be solved before v1.0. Free beer for whoever can finish either of the above tasks before me.

Author

License

Apache 2.0, See the file LICENSE
Copyright © 2011 Michael Garriss

No comments:

Post a Comment