repo page on github.com
First a complete example:
require 'named_value_class'
NamedValueClass Pitch:Fixnum, constrain:0..11 do
minus_a :Interval do |lhs,minus,rhs|
result = minus.call(rhs) % 12
is_sharp? ? Pitch.sharps[result] : Pitch.flats[result]
end
all_operators_with_a :Pitch, raise:SyntaxError
def as_flat
self.class.naturals[self] || self.class.flats[self]
end
def as_sharp
self.class.naturals[self] || self.class.sharps[self]
end
end
Pitch Bs: 0, sharp:true
Pitch C: 0, natural:true
Pitch Cs: 1, sharp:true
Pitch Db: 1, flat:true
Pitch D: 2, natural:true
Pitch Ds: 3, sharp:true
Pitch Eb: 3, flat:true
Pitch E: 4, natural:true
Pitch Fb: 4, flat:true
Pitch Es: 5, sharp:true
Pitch F: 5, natural:true
Pitch Fs: 6, sharp:true
Pitch Gb: 6, flat:true
Pitch G: 7, natural:true
Pitch Gs: 8, sharp:true
Pitch Ab: 8, flat:true
Pitch A: 9, natural:true
Pitch As: 10, sharp:true
Pitch Bb: 10, flat:true
Pitch B: 11, natural:true
Pitch Cb: 11, flat:true
NamedValueClass Pitch:Fixnum, constrain:0..11 do
minus_a :Interval do |lhs,minus,rhs|
result = minus.call(rhs) % 12
is_sharp? ? Pitch.sharps[result] : Pitch.flats[result]
end
all_operators_with_a :Pitch, raise:SyntaxError
def as_flat
self.class.naturals[self] || self.class.flats[self]
end
def as_sharp
self.class.naturals[self] || self.class.sharps[self]
end
end
Pitch Bs: 0, sharp:true
Pitch C: 0, natural:true
Pitch Cs: 1, sharp:true
Pitch Db: 1, flat:true
Pitch D: 2, natural:true
Pitch Ds: 3, sharp:true
Pitch Eb: 3, flat:true
Pitch E: 4, natural:true
Pitch Fb: 4, flat:true
Pitch Es: 5, sharp:true
Pitch F: 5, natural:true
Pitch Fs: 6, sharp:true
Pitch Gb: 6, flat:true
Pitch G: 7, natural:true
Pitch Gs: 8, sharp:true
Pitch Ab: 8, flat:true
Pitch A: 9, natural:true
Pitch As: 10, sharp:true
Pitch Bb: 10, flat:true
Pitch B: 11, natural:true
Pitch Cb: 11, flat:true
What does this do?
First create a new class named Pitch that delegates most work to Fixnum with a value between 0 and 11 inclusive; Open a block to define some specialized methods:
NamedValueClass Pitch:Fixnum, constrain:0..11 do
Define how to handle the "Pitch - Interval" use case:
minus_a :Interval do |lhs,minus,rhs|
When defining operators the blocks are always yieled: the LHS (left hand side), a proc with the original, default implementation of the operator you are redefining, and the RHS. The passed proc's receiver is the LHS so in the following code "minus.call(rhs)" performs "lhs - rhs".
result = minus.call(rhs) % 12
Since we plan on setting boolean parameters named 'sharp' and 'flat' when we define Pitch constants later we can use methods is_sharp?, is_flat?, Pitch.sharps(value) and Pitch.flats(value):
is_sharp? ? Pitch.sharps[result] : Pitch.flats[result]
It doesn't make any sense to do math with a Pitch on the left and right sides so all operators should raise a SyntaxError. Example: Ds + C
all_operators_with_a :Pitch, raise:SyntaxError
Define some instance methods:
def as_flat
self.class.naturals[self] || self.class.flats[self]
end
def as_sharp
self.class.naturals[self] || self.class.sharps[self]
end
self.class.naturals[self] || self.class.flats[self]
end
def as_sharp
self.class.naturals[self] || self.class.sharps[self]
end
Now create Pitch constants:
Pitch Bs: 0, sharp:true
Pitch C: 0, natural:true
Pitch Cs: 1, sharp:true
Pitch Db: 1, flat:true
Pitch D: 2, natural:true
Pitch Ds: 3, sharp:true
Pitch Eb: 3, flat:true
Pitch E: 4, natural:true
Pitch Fb: 4, flat:true
Pitch Es: 5, sharp:true
Pitch F: 5, natural:true
Pitch Fs: 6, sharp:true
Pitch Gb: 6, flat:true
Pitch G: 7, natural:true
Pitch Gs: 8, sharp:true
Pitch Ab: 8, flat:true
Pitch A: 9, natural:true
Pitch As: 10, sharp:true
Pitch Bb: 10, flat:true
Pitch B: 11, natural:true
Pitch Cb: 11, flat:true
Pitch C: 0, natural:true
Pitch Cs: 1, sharp:true
Pitch Db: 1, flat:true
Pitch D: 2, natural:true
Pitch Ds: 3, sharp:true
Pitch Eb: 3, flat:true
Pitch E: 4, natural:true
Pitch Fb: 4, flat:true
Pitch Es: 5, sharp:true
Pitch F: 5, natural:true
Pitch Fs: 6, sharp:true
Pitch Gb: 6, flat:true
Pitch G: 7, natural:true
Pitch Gs: 8, sharp:true
Pitch Ab: 8, flat:true
Pitch A: 9, natural:true
Pitch As: 10, sharp:true
Pitch Bb: 10, flat:true
Pitch B: 11, natural:true
Pitch Cb: 11, flat:true
An irb session with the above code loaded:
>> load './pitch_class.rb' #=> true
>> include Pitch::NamedValues #=> Object
>> C #=> C
>> Eb #=> Eb
>> [C,Eb] #=> [C, Eb]
>> C > Eb #=> false
>> C.value #=> 0
>> Eb.value #=> 3
>> C + 2 #=> D
>> Db + 7 #=> Gs
>> Db.is_sharp? #=> nil
>> Db.is_natural? #=> nil
>> Db.is_flat? #=> true
>> Db.is_insane?
NoMethodError: undefined method `is_insane?' for Db:Kernel::Pitch
>> Pitch H:0, insane:true #=> H
>> Db.is_insane? #=> nil
>> H.is_insane? #=> true
>> Pitch.flats #=> {1=>Db, 3=>Eb, 4=>Fb, 6=>Gb, 8=>Ab, 10=>Bb, 11=>Cb}
>> include Pitch::NamedValues #=> Object
>> C #=> C
>> Eb #=> Eb
>> [C,Eb] #=> [C, Eb]
>> C > Eb #=> false
>> C.value #=> 0
>> Eb.value #=> 3
>> C + 2 #=> D
>> Db + 7 #=> Gs
>> Db.is_sharp? #=> nil
>> Db.is_natural? #=> nil
>> Db.is_flat? #=> true
>> Db.is_insane?
NoMethodError: undefined method `is_insane?' for Db:Kernel::Pitch
>> Pitch H:0, insane:true #=> H
>> Db.is_insane? #=> nil
>> H.is_insane? #=> true
>> Pitch.flats #=> {1=>Db, 3=>Eb, 4=>Fb, 6=>Gb, 8=>Ab, 10=>Bb, 11=>Cb}
rubygems.org page
repo page on github.com
now that this is "done" i can get back to peas. 0.3 should be out by tomorrow.
ReplyDelete