More Ruby Awesomeness Pt. 3

Part two.

4. Few true operators and keywords

There are few actual operators in Ruby. Most are methods. = is an operator.

+, -, <, [], {}, etc are methods that you can add to any class. You can not override operators since they are not methods, but you can add many types of methods that look like operators and look like part of the overall language. There are examples above that demonstrate it.

As already noted, the “keyword” attr_accessor is a method, not a keyword. This is powerful despite seeming like it is just syntactic sugar. Many keyword-like features can be added to the language in a first-class way. These “keywords” can be overridden. I think that being able to seemingly extend the language and make it look like it is a first-class citizen is useful.

Many keywords are methods
class Test
 method(:attr_accessor).class
end

#Output
Method

As shown in the next section, everything in Ruby returns a value, so pasting this small class into the REPL console, Method is returned. As will be shown, you can store the value of that class next.

5. Everything is an expression

“Everything is an expression” is a simple concept that yields powerful results.

There are no statements in Ruby.

Everything returns a value, even if it is not helpful, and sometimes it is not. What gets returned is the last value that is evaluated in the expression. Because of this, the keyword ‘return’ in methods is not always mandatory.

The return keyword is necessary when returning early and returning multiple individual values. Ruby automatically packs multiple values into an array for you unless you specifically give it multiple variables to return.

In this context conditionals, loops, methods, and well everything return a value.

Everything is an expression
value = class Test
 method(:attr_accessor).class
end

puts value
=>Method

#this is not useful information
puts 'print'
=>nil

#This is completely redundant, but conditionals return a value which is the last thing evaluated
a='string'
value  =if a.nil?
 true
else
 'not true'
end
puts value
=> 'not true'


#Methods return the last line evaluated, typically negating the need for 'return'
def do_something
1+4
end

puts do_something
=>5

#But you can add return if you like it redundantly explicit
def do_something
return 1+5
end

#If you need to return early, it is required
def do_something_with_a_number arg
 return -1 if arg==0
 arg * 100
 end

 do_something_with_a_number 10
 => 1000
 do_something_with_a_number 0
 => -1

 #Multiple values can be returned delimited by a comma and ruby will pack it in an array, but return is required
 def multiple_return_values
 return 42, 'forty two', :forty_two
 end

 multiple_return_values
 =>[42, 'forty two', :forty_two]

 #Or, you can use multiple variables to store them
a,b,c = multiple_return_values
puts a
=> 42
puts b
=>forty two
puts c
=> :forty_two

#If there are not enough variables to store them, the remaining will
#be packed into an array and stored in the last variable
a,b=multiple_return_values
puts a
=> 42
puts b
=>['forty two', :forty_two]

If there are too many variables to store the return values, the extra variables will point to nil.

6. Blocks, Procs, and Lambdas

These constructs allow you to alter what a method does at runtime. They are interrelated, with Procs and lambdas looking the same on the surface. These will “close over” all variables in scope when they are called and bring them along to the method it is being passed. In short, they have the property known as closures. Procs and lambdas look very similar but have important and confusing differences. It is not always clear why both exist.

Blocks are objects but you can not directly interact with them like a normal object unless you convert them to a Proc. These are passed into methods. There is an implicit reference for them and they are executed with the yield keyword. Blocks are commonly used to change the behavior of a method at runtime, such as how to sort an array or use map() on it.

Basic usage of blocks on data structures
array = [3,6,2,7,8,5]

#default sort
array.sort
=> [2,3,5,6,7,8]

#force it to sort in reverse
array.sort {|a,b| b<=>a}
=>[8, 7, 6, 5, 3, 2]

#the reference array has not been changed. A copy of the sorted array is returned in the sort call
puts array
 => [3, 6, 2, 7, 8, 5]

#Force the array to sort itself
array.sort!
=>[2,3,5,6,7,8]

puts array
[2,3,5,6,7,8]

#A simple example of map, multiply each element by 16 for some reason...
array.map {|i| i*16}
=>[32, 48, 80, 96, 112, 128]

The ‘!’ at the end of a method name denotes that it is destructive. That is, it mutates the object. For the most part, if it is not appended with the ‘!’, it will return a new object.

The exception, and it is annoying, is that methods in the standard library that do not have both a destructive and non-destructive method will not have the ‘!’ appended to a destructive method. That bugs me so much.

The non-destructive versions are what you want most of the time. Most definitely, if you are writing in a more functional style.

Side-effects are bad, mkay?

The final example in this essay makes use of lambda. This is a subject deserving of an essay, so is on the to-do list.

7. Modules and Mixins

Modules are groupings of related code, and yes, a module is an object. Methods in modules can be used as “static” methods or designed to be added to other objects dynamically. These are called mixins.

Module methods could be thought of as Java static methods and accessed as such.

Modules are objects but can not be instantiated. Modules can be used to hold classes as a way to namespace them, so classes with the same name will not conflict.

Module methods
 module Test
   def self.testing
    puts 'testing'
   end

   class NamespacedClass
     def to_s
       'NamespacedClass'
     end
    end # end of class
 end # end of module

  Test.testing
  => testing

  Test::NamespacedClass.new.to_s
  => NamespacedClass

Since Ruby does not support multiple inheritance. Mixins, along with constructs such as singleton methods, add back the advantages of multiple inheritance without the drawbacks.

I think the fear of multiple inheritance is massively overstated, much like gotos in C. This is what Java should have had from day one, instead of interfaces. I guess Java has something similar to mixins now, just very stiff and more verbose than it needs to be, which is Java in a nutshell. My decision to never write Java again might be the best decision I ever made. Other than permabanning PHP. I still like the JVM and will write code in other languages to run in the JVM.

I don’t think it is a good idea, but you can define object variables in a mixin that can be accessed in typical object methods and vice-versa.

Mixins
 module Test
   def testing
     @val='mixin variable'
     puts 'mixin testing'
   end
 end

 class MixinTest
   include Test

   def obj_method
     puts @val
   end
 end

 t=MixinTest.new
 t.testing
 t.obj_method

 #Output
 mixin testing
 mixin variable

Both types of methods can exist in a single Module.

8. Open to changes during runtime

Earlier, we added a singleton method to an object. Here we will add two to a class so that all instances of that class can use them. Let’s add the methods thousand, million, and billion to the built-in Numeric class.

Classes are open to changes at runtime
class Numeric
  def thousand
    self * 10**3 # 10^3
  end

  def million
   self * 10**6 
  end

  def billion
    self * 10**9
  end
end

42.million
=>42000000

42.billion
=>42000000000

#A use case for this might be easier looping here is a method to loop from one value, increment by one each loop until it hits the end
10.thousand.upto(1.million) do |val| # do / end is the typical way to write multiline blocks but is not enforced, {} works also
 puts val
end
#This will print every value from 10,000 to 1 million
#Most looping methods are built into the object or part of modules like Enumerable

Load the file this is in as part of the program startup, and it will be available everywhere in the program. This is the most straightforward way to do it. Opening the eigenclass is another way and will be talked about in the series on the Ruby object model.

Opening class is extremely useful in all sorts of contexts. Some libraries use this for date and time management, such as 5.days.ago and things like that. It keeps a clean interface at the cost of cluttering up the implementation a little bit.

For part of completing my master’s degree, I wrote a program that took a lot of popular security and network tools. It used the output of one tool as input for others and merged it into a semi-coherent platform to automate security testing. It would then print out a detailed report with little human interaction.

To facilitate creating reports, I developed a library to handle all of that messiness. It can print out to screen, text, and PDF, and also HTML. It runs as a singleton object, initialize() loads the expected format module but only one, and if needed, when a different format is requested the object would mutate and print in the new format.

Yes, I could have had multiple classes such as TextPrinter, PDFPrinter, etc but where is the fun in that? A single ReportPrinter class was all that was needed. Any specialized code was pushed out in modules meant to be mixed in. If I had multiple print classes all of that would be subclassed or the code repeated, again and again. In hindsight, it would have been better if ReportPrinter was the mixin to the format classes, but the way I did it worked out well.

One object of ReportPrinter was all that was required. Modules could be added and removed from the object at runtime when the situation called for it.

At first glance, it looks like it might be weird for the sake of being weird, but there were three reasons I did it this way. It was fun, it turned out to be very flexible, and my thesis adviser wanted me to add in some creative examples of Ruby since she was unfamiliar with the language.

It turned out well. At some point in my project, I was told that it needed a way to print out the report in HTML for some unfathomable reason - at least she didn’t ask for a Word format print feature. Doing so required a single module for the HTML formatting, and nothing else needed to be changed. There was already a format query tool that I wrote that just picked it up and offered it as an option in the user interface with nothing extra to do on my part. It is just seamless on the part of the programmer and user of my hacking tool.

It is very useful in many contexts. I have thought about releasing it as open-source. It was the last part of my master’s project, so it is a bit messy, and I had to start writing a paper describing the project, which took a while since it ended up at about 110 pages. I only had to do a project or a thesis, but it somehow turned into both.

The project almost got out of control because, as usual, I overdo things and get way over-optimistic. If I had not listened to one of my professors who read my proposal, I would be working on it today. Nearly 12 years after starting it, because the scope of the project was initially insane. Even so, I let a few features removed for sanity to creep back in, so the project and thesis took about two years before it was ready for the presentation and defense.

If you are curious, the user interface was in standard Java using Swing. The actual working code is just plain Ruby files. The Swing library was even less annoying than normal to use since I was able to leverage blocks in passing functionality to the Swing widgets. JRuby would inject the ruby code into the GUI handler methods in the Java classes. At the time that I was writing my thesis, Java had no sane way to use closures. Ruby blocks allowed me to avoid the insanity of anonymous inner classes and keep the line of code count way down in the Swing code. For instance, I only needed one Button class and one MenuItem class instead of dozens of files for each.

I also used the excellent mig layout library, further reducing the complexity of the Java code. Just thinking about Java makes me want to vent my seething hatred for that overly verbose mess. Love the Java runtime environment. I hope Ruby’s runtime can get as advanced and awesome at some point, or at least somewhat close to it. It is why I am interested in the Kotlin language, but that is for another article.

One more part!