Rosettas‎ > ‎

Ruby


# Copyright (c) 2007 Art & Logic, Inc. All Rights Reserved.
# $Id: Rosetta.rb,v 1.0 2009/11/20 10:13:57 tmorgan Exp $

# Above: [sf02], [sf03]


# a note on the stuff inside the square brackets:
# Each of the codes inside brackets is a reference to the rule(s) in the
# Style Guide that's being illustrated, as follows:
# [sf##] - StyleGuide/SourceFiles
# [cas##] - StyleGuide/ClassesAndStructures
# [nr##] - StyleGuide/NamingRules
# [cg##] - StyleGuide/Commenting
# [fmt##] - StyleGuide/Formatting

# Throughout: [fmt01]; all indentation done with space characters (0x20),
# three spaces per logical tab stop.

# [nr21] C/C++ guard to prevent recursive inclusion of header files
# !!! no separate syntax is necessary to prevent this; however, to ensure
# that files are only loaded once, all includes should utilize the 'require'
# syntax in ruby (see [sf05] below). !!!

# [sf05]
require 'SomeOtherRubyFile'

# [nr25] the 'q' prefix indicates that we are controlling conditional
# compilation
# !!! Ruby is an interpreted language, so there is no need for syntax
# to control conditional compilation. !!!

# [sf07] while Ruby allows for any number of class/module definitions per
# source file, it is preferred to have a single class per file, barring
# exceptions noted in [sf07].

# [nr18] All class names should begin with a capital letter "A," followed
# immediately by another capital letter (or digit) that begins the 'real'
# name of the class.
# !!! Ruby - Modules in Ruby can be used as 'pseudo-classes,' interfaces, or for
# namespacing. When using Modules, the naming convention should be appropriate
# to the context in which the Module is used (a Module used as a Class, for
# example, should be named AModule. etc.) !!!

# [cg03] Comments should be formatted with the appropriate markup for
# automatic document extraction tools (for Ruby, this is likely to be RDoc).

# [cas01] public member functions/methods listed first
# !!! Ruby - should be listed after initialize function (constructor) for
# clarity !!!

# [nr20] global variables are prefixed with a lowercase g, followed by an
# uppercase letter beginning the variable name. Avoid using global variables.
# !!! Ruby - if a global variable is used, it is preceded with $. Thus, the
# leading g is unnecessary. !!!
$globalVariable = "Please don't use global variables."

##
# ARosetta
# Simple, but meaningless class to demonstrate Art & Logic programming
# style conventions as they are applied to the various programming languages
# we use. In several places, you're asked to imagine a block of useful code
# to avoid distracting you from the issues of formatting (etc) that are being
# demonstrated.
#
# As you've already seen above, the thing about to be shown in this file is
# called out in comments using the A&L-style markup '!!!'
class ARosetta
   # [nr08] new non-class types (enumerations, data structures, etc.) are
   # named with MixedCase and no special prefix
   # [nr23] identifiers specifying constants for the program are always
   # prefixed with a lowercase 'k'.
   # !!! Ruby - no enumeration syntax, and constants are required to start
   # with an uppercase letter in Ruby. In following with that, start all
   # constants with an uppercase K, followed by another capital letter
   # (or digit) that begins the 'real' name of the constant. !!!
   KSomeConstant = 50
   KSomeOtherConstant = 100

   ##
   # Construct the Rosetta object.
   def initialize
   end

   # !!! Ruby - no destructors in Ruby !!!

   # [nr07] Member functions are named using CamelCase, with a leading upper
   # case letter.
   # !!! Ruby - functions must be named with a leading lower case letter. !!!
   def memberFunctionWithMultipleWords
      ok = true
      # [fmt08] -- formatting of a for loop.
      for i in (0..KSomeConstant)
         for j in (0..KSomeOtherConstant)
            # [fmt02] -- preprocessor directives go in column zero.
            # !!! Ruby - no preprocessor directives. Logging verbosity should
            # be handled by the Logger implementation (initialization assumed
            # for the sake of this example) !!!
            #
            # !!! Ruby - method calls in Ruby do not require that parameters be
            # grouped inside parentheses. For clarity and consistency purposes,
            # however, parentheses should always be used to indicate method
            # parameters when they are present.
            OurLogger.log.debug("testing: #{i}, #{j}")
            
            # [nr07] -- Calls to member functions of the current object are
            # made through an explicit self pointer.
            # !!! Ruby - there is one exception to this rule, which is that
            # private methods can't be accessed through the use of a self
            # pointer, due to the language architecture. Public and protected
            # methods should use self where applicable, however.
            ok = self.test(i, j)
            unless ok
               OurLogger.log.error("Fatal error test: #{i}, #{j}")
               break
            end
         end
         unless ok
            break
         end
      end
      
      # !!! In Ruby, return statements are not required; the result of the
      # last line of code executed is always returned. However, for consistency
      # and clarity, an explicit return should be used.
      return ok

      # [fmt02] -- labels used as the target of a goto are placed in column
      # zero.
      # !!! Ruby - no goto or labels. !!!
   end

   ##
   # !!! Here's how we handle very long lines (i.e., longer than 79 characters)
   def longLineWrapping
      # [fmt03] -- source lines should be less than 80 characters long.
      # [fmt04] -- break lines at the most logical point.
      #  1         2         3         4         5         6         7
      #890123456789012345678901234567890123456789012345678901234567890123456789
      retval = SomeStaticClass.functionWithManyArguments(longArgumentName1,
       longArgumentName2, longArgumentName3, longArgumentName4)
      
      # alternative format #1
      retval = SomeStaticClass.functionWithManyArguments(longArgumentName1,
       longArgumentName2,
       longArgumentName3,
       longArgumentName4)

      # alternative format #2
      retval = SomeStaticClass.functionWithManyArguments(
       longArgumentName1,
       longArgumentName2,
       longArgumentName3,
       longArgumentName4)

      # [fmt05] -- prefer to break after a comma
      retval = SomeStaticClass.someUsefulFunction(x.leftMargin,
       x.topMargin + x.bottomMargin - 1)

      # [fmt05] -- if that's not possible, break after an operator
      retval = SomeStaticClass.someUsefulFunction(x.leftMargin +
       anObject.horizontalPadding, x.topMargin + x.bottomMargin - 1)

      return retval
   end

   ##
   # !!! Loop formatting !!!
   def loops
      # [fmt08] for loop
      for i in (0..kSomeConstant)
         # loop code here...
      end

      # [fmt09]
      while someBooleanExpression
         # useful code...
      end

      # [fmt09] -- while loop with an empty body makes the emptiness explicit
      while self.someFuncReturningBool
         # intentionally empty...
      end

      # [fmt10] -- do/while loop.
      # Ruby does not explicitly include a do/while loop, the following syntax
      # may be utilized to achieve the same effect.
      begin
         # loop some code here...
      end while someBooleanExpression

      # !!! A common method for looping in Ruby is to make use of the 'each'
      # method (or other methods that iterate over a collection). An example
      # of this syntax is provided, although not covered explicitly by a rule
      # in the general style guide.
      #
      # Note that the do/end block is actually an argument to the each method on
      # someCollection. In that respect, it is a necessary exception to [fmt18].
      # !!!
      someCollection.each do |collectionItem|
         # do something with collectionItem
      end
   end

   # [cas01] Protected member functions declared after public.
   # !!! Ruby - protected is a keyword - all method/constant declarations
   # following the use of this keyword are considered protected until another
   # scoping keyword is used. !!!
   protected

   def test(firstVal, secondVal)
      retval = true
      # [fmt07] if/else formatting
      if (firstVal < secondVal)
         # [cg06] -- use '$$$' as a marker to indicate a request that the
         # following block of code be reviewed before a change is considered
         # complete
         # $$$ before
         # process(secondVal)
         # $$$ after
         # Shouldn't this be processing the first value in this case?
         process(firstVal)
         # $$$ end
      elsif (secondVal > KSomeConstant)
         process(secondVal)
      else
         # [fmt18] -- use curly braces even in cases where the language doesn't
         # require it to prevent maintenance errors.
         # !!! Ruby - due to syntax differences and the requirement that 'end'
         # is always used to indicate the termination of a control block, this
         # rule is enforced as part of the language. !!!
         retval = false
      end

      return retval
   end

   # [cas01] Private member functions declared after protected.
   # !!! Ruby - as with protected, 'private' is a keyword designed to alter the
   # scope of all method/constant declarations that follow. !!!
   private

   def process(val)
      # [cg05] -- use '!!!' as a marker to indicate a section of code that
      # requires attention
      # !!!... removing old hardcoded value
      # fProcessResult = 131
      # ...!!!
      result = val
      while (val -= 1) > 0
         result *= val
      end
      @processResult = result
   end

   def decide(midiEventType)
      # [fmt11] - switch/case formatting.
      case midiEventType
         when KNoteOn
         self.noteOn(now)
         if (duration > 0)
            self.noteOff(now+duration)
         end

         when KNoteOff
         self.noteOff(now)

         when KPolyAfter
         after = channelValue
         # [fmt35] include, but comment out the break when falling through
         # intentionally.
         # !!! Ruby - no break is required for case statements in ruby, as
         # falling through is prohibited by the language implementation.
         # Therefore, this formatting rule is not applicable.

         when KChannelAfter
         self.afterTouch(after)

         when KPitchBend
         # do nothing...
         
         # [fmt12] switch statements should always have a default case.
         else
         # !!! Ruby - no native support for asserts. An assert method could
         # be added to the Ruby Kernel module and executed based on the
         # presence of a debug command-line parameter; however, this would
         # probably be better accomplished through other means.
         # assert(false)
         OurLogger.log.error("Unsupported event type!")


         # !!! Ruby - in Ruby libraries, it is common form to use a Dictionary as a
         # method argument in order to make code more 'readable' and to allow for
         # an arbitrary number of arguments. It is the recommendation of this guide
         # that all such Dictionaries are clearly marked with {} syntax, as opposed
         # to the more ruby-conventional approach of leaving the brackets off.
         # Examples below:
         retval = SomeStaticClass.badDictionaryExample(a, b, c => d, e => f, g => h)
         retval = SomeStaticClass.goodDictionaryExample(a, b, { c => d, e => f, g => h})
         # !!!
      end
   end

   # [cas02] member variables declared in the same order: public, protected,
   # private.
   # !!! Ruby - in ruby, all member variables (referred to in ruby as instance
   # variables) are private by definition; there is no way to declare a public
   # or protected instance variable. In the case of public variables, the ruby
   # syntax for creating an attribute accessor should be used (see modValue,
   # below). The attribute accessor syntax creates an instance variable with the
   # given name, as well as public set/get methods for that variable. !!!
   #
   # [nr04], [nr19] -- class member variables always begin with a lowercase f 
   # followed by a single uppercase letter.
   # !!! Ruby - in ruby, member variables are required to be preceded by the '@'
   # symbol. In this way, the use of a lowercase f to denote that the value is a
   # member variable is not necessary.
   # It's also the case in ruby that member variables do not need to be declared
   # before they are used. However, for the sake of clarity and consistency, all
   # member variables should be defined (even if they are initialized to nil).
   # !!!
   attr_accessor :modValue
   
   @filename = nil
   
   # [fmt28] -- when declaring pointer variables, attach the '*' to the
   # typename, not the variable name.
   # !!! in Ruby, all variables are "pointers," so special consideration for
   # pointers is not necessary. !!!
   @inputFile = nil

   @processResult = 0

end