Sunday, November 24, 2013

From Java to Ruby

From Java to Ruby
November 24th, 2013

I've been a java programmer for a long time. I'm used to all its ins and outs.

I've been learning ruby & rails over the last month. Yesterday I sat down to write a complex batch process and it became apparent that I was missing some basic understanding of the ruby language. Not the syntax but how to structure code.

There are plenty of sites out there that cover a variety of topics but I thought I'd throw in my 2 cents on how to think about ruby from a java developer's mindset.

Audience:
This is intended for Java developers new to Ruby. I'm new to Ruby and some stuff has baffled me. Anything that gave me grief is covered here. I started with "Agile Web Development with Rails". Great for building web applications but it glossed over some of the finer points with Ruby.

--------------------------------------------------------------------------------------------------------
Safety:
--------------------------------------------------------------------------------------------------------

Java provides compile time safety that doesn't exist in Ruby. This leads to more verbose code in java but you know exactly where everything is coming from. Not so with Ruby. As an example, I added a table via generate scaffold called Resource. When it came to running the functional test this caused a recursive stack trace failure because there was a deep down use of "resource" in a different context. Debugging that was a total pain in the ass because stack traces overflows don't provide stack traces.

What is Ruby's answer to compile time safety issues? Imho: Testing. You may have been able to get away with not writing test cases for your java code. If you don't write test cases for your Ruby code who knows what will break and when. Tests are your insurance against weird stuff happening in production.

--------------------------------------------------------------------------------------------------------
Polymorphism: Duck Typing
--------------------------------------------------------------------------------------------------------

Everyone has a different idea of polymorphism. In java it is primarily interfaces. Classes agree to contracts (via interfaces) that other classes can rely upon and this is ensured at compile time.

That doesn't exist in Ruby. It is best to stop using the term "polymorphism" when you work with ruby. A more accurate term is duck typing.

What is duck typing?
It is actually very simple idea: If I try to call a static or non-static method on an object or class and it exists, it works. Period.

Yea you heard me, you can name a method the same on two unrelated classes. That method can be non-static OR static. If it exists it will work.

This leads to some incredible flexibility and danger at the same time. Something that is fun to write and possibly a nightmare to debug when the shit hits the fan. Yes... this goes back to testing. Testing is your compiler and you better have complete tests to ensure stuff doesn't break.

Why is it called duck typing?
If you have an object in your hand that you think is a duck, you call the quack() method on it. If it doesn't quack it throws a run-time exception.

--------------------------------------------------------------------------------------------------------
Scope/Namespacing:
--------------------------------------------------------------------------------------------------------

Ruby : File != Class
In java each file is a class. Not so with Ruby. In Ruby you can do whatever you want in a file. Define a module, define 5 classes, run code if you call it. It feels like javascript in this context. Although this is true, there are rails conventions I will cover in a bit that feel a lot like java conventions.

Java : Folders = Scope
In java, everything is organized via a tree of folders. The folders are the package and it is very clear where stuff is and how it is scoped. If you want to include a package you import it and you know that the package is also the folder structure where the code lives.

Ruby : Folders != Scope
Ruby breaks apart folders from the scope of classes/methods.You put ruby files into folders for physical organization BUT you don't refer to those folders in your code. You put them on the path only. You will refer to a file via load() and require() but that is different that scoping code.
You scope code via Modules. This is referred to as namespacing.

Ruby: Scope = Modules
Modules are a weird beast that serve 2 purposes. I've seen complaints on the web that java interfaces serve 2 purposes (marker and contract). Well, Modules serve 2 purposes: scope and mixins. Scope is similar to packaging. It allows you to organize your classes. It is different from java in that the module hierarchy is defined in the code, not via folder structure. Example:

module MyModule
  module MySubModule
    class MyClass
    end
  end
end

You would then refer to your class via MyModule::MySubModule::MyClass. This is actually more verbose than java because imports are usually handled by your IDE and shoved at the top. In java you just say MyClass and the import above defines where it came from. You can force it to be MyPackage.MyClass if you have collision but that is uber rare.

--------------------------------------------------------------------------------------------------------
Rails & the lib directory
--------------------------------------------------------------------------------------------------------

I'm used to testing simple code in java by writing a call in the main and then passing parameters to it to configure logging/database/etc. In rails land you are running the code within the rails environment and it takes care of that for you.

Now I know I said that ruby files can contain anything. Well... if you are going to want your modules & classes to be autoloaded by rails, it appears you need to follow rail's folder and file naming conventions. I'm not 100% sure this is best practice, but it works.

lib/module_one/module_one_sub/my_class.rb

my_class.rb would look like this:

module ModuleOne
  module ModuleOneSub
    class MyClass
      def self::talk
        puts "hello"
      end
    end
  end
end

You also need to tell rails to autoload your code in the lib directory. Edit config/application.rb and add this line in the Application class:
config.autoload_paths += %W(#{config.root}/lib) 

Once you have that in place your can test your code from the "Rails Console".
RubyMine : "Tools : Run Rails Console"
Command Line : rails console

In this case I would type:
> ModuleOne::ModuleOneSub::MyClass::talk

So... even though ruby lets you do whatever you want, if you want to do it the rails way you need to follow conventions that are similar to java.

--------------------------------------------------------------------------------------------------------
Ruby Module Mixins
--------------------------------------------------------------------------------------------------------

Mixins are a ruby specific concept. There is no java equivalent for them. To try to relate them to a java concept like abstract classes is a bad idea. They are Ruby's restricted answer to Multi-Inheritance just as Interfaces are Java's restricted answer to Multi-Inheritance. Interfaces and Modules are completely different beasts but both languages wanted to provide a form a MI. They just chose to do it differently.

Think of mixins as methods & variables you can include into a class as either static or non-static outside of the inheritance tree. WTF did I just say? Yea, I know, that is some weird magic sauce.

Here's what you do:
1. Define variables and methods within a module.
2. To use them you include/extend the module into your class when you define the class.
> If you include the module the variables and methods are non-static (scoped to an object instance).
> If you extend the module the variables and methods are static.

Conceptually you can use mixins for stuff like comparables and iniitializations. I don't know an uber good use for them yet so I'm going to avoid them for the moment.

One other thing...
You can call a method on a module by declaring it as a function:
module SimpleModule
  def my_module_method
    puts "I will be able to be called directly on module"
  end
  module_function :my_module_method
end

In this example my_module_method() can now be called on the module and it can NOT be used as a mixin on a class. If you try to call it you will get a permission issue.

You can also do it this way:
def self.module_function_put
  puts "I can be called directly on module"
end

I have a bad feeling about module functions. Ruby tends to allow you to do pretty much whatever you want. This means you can use its flexibility for good or for evil. Just because you can do something doesn't mean you should. An alternate approach is to do this:
Class.new.extend(SimpleModule).my_module_method

That gives you the flexibility you want without restricting the method onto the module.

Keeping include & extend straight in your head
How do I keep extend & include straight in my head? If you turn a module method into a function it behaves like a static method on the module. So... if you are extending a module method you are kinda extending static methods.

--------------------------------------------------------------------------------------------------------
Variable Scope
--------------------------------------------------------------------------------------------------------

Ruby has some strange behavior with variable scoping. In java, variables defined within if/else blocks are scoped to those blocks. Not so with Ruby...

There are 2 cases and they behave differently:
1. variables defined within if/else blocks ARE available outside of the if block
2. variables defined within a execution block are NOT available outside of the block BUT the execution block can access variables outside of itself.

Woa there. wtf was that? Here are examples to clarify:

# example #1
foo = true
if foo
  bar = 'hi there'
end
puts bar # this works

# example #2
foo = true
mylist.each do |entry|
  puts foo # this works
  bar = entry
end
puts bar # this does NOT work

--------------------------------------------------------------------------------------------------------
Public Static Void Main
--------------------------------------------------------------------------------------------------------

Exactly where is the main for ruby? Well, it doesn't exist. If you have code in your ruby file and you run it... it runs. If the file is only module/class definitions, nothing is going to happen but if you have code floating outside of those, it will run it.

Having said that, there is a trick to only running code within the file you ran:
if __FILE__ == $0
  puts 'hi there. only ran if this file was the one run'
end

--------------------------------------------------------------------------------------------------------
Including other ruby code
--------------------------------------------------------------------------------------------------------

I'm still new at this so this is something I'm still trying to get my head around. In java you have a classpath and you can refer to anything in that classpath via package imports.

In ruby you have 2 options:
1. include a specific file on a hardwired path (load method)
2. include a specific file that is found on the ruby path (require method)

Examples:
# load file (path variable not used, multiple calls allowed)
load 'my_ruby_file.rb'

# load file from path (auto-prevents reloading loaded file)
require 'my_ruby_file'

Oh, and if you want to modify the require path to include the current directory use this:
$:.unshift File.dirname(__FILE__)

I fought with require a LONG time before I figured this out

Rails versus Ruby inclusion:

It is important to keep in mind that rails does some back magic for you with library inclusion. With rails, you add a library directory and all files that follow the rails module/class naming convention are loaded.

Example:
lib/my_module/my_class.rb

Rails will automatically find "my_class.rb" for inclusion even though it is in a subdirectory within lib. In fact, you don't even need to do a require for "my_class" because rails has already done that for you.

Where I get tripped up is trying to run library code within a ruby script. If you want to include my_class from a ruby script you don't add the lib directory to your path. You must include lib/my_module on your path.

Example in a test ruby script. I put it here: script/tester.rb:
$:.unshift File.dirname(__FILE__), '../lib/my_module'
require "my_class"


--------------------------------------------------------------------------------------------------------
Inheritance
--------------------------------------------------------------------------------------------------------

Well, at least inheritance is normal. I'm not sure about dealing with multi-argument inheritance but the basics are the same as java.

Example:
class SimpleClassInherit < SimpleModule::SimpleModuleClass
end

--------------------------------------------------------------------------------------------------------
Last Comments
--------------------------------------------------------------------------------------------------------

Did I get everything right?
Probably not but I wanted to lend a hand. If something gave me trouble or caused me a lot of digging, I tried to cover it here.

What do I think of Ruby versus Java?
Java feels like a tightly controlled tree of code where control flows through its known branches
Ruby feels like a collection of stacks of cards that are mixed together at run-time.

--------------------------------------------------------------------------------------------------------
A Sample Class
--------------------------------------------------------------------------------------------------------

I like to keep a printed copy of a sample class that does a little of everything. Here you go:

# load file (path variable not used, multiple calls allowed)
#load 'my_ruby_file.rb'

# load file from path (auto-prevents reloading loaded file)
#require 'my_ruby_file'

module SimpleModule

  # method scoped to a module for include/extend
  def module_scoped_put yeScope
    puts "I have been #{yeScope} called on a class"
  end

  # module_function: turns module method into a function (can be called directly on module)
  # > Note that this no longer is available for public use on class
  def module_scoped_put_two
    puts "I will be able to be called directly on module"
  end
  module_function :module_scoped_put_two

  # method scoped to a module for directly calling it
  def self.module_function_put
    puts "I can be called directly on module"
  end

  # class namespaced within a module
  class SimpleModuleClass

    # normal non-static method
    def class_scoped_non_static_put
      puts "SimpleModule::SimpleClass NON-STATIC hello world"
    end

    # static/class method
    def self::class_scoped_static_put
      puts "SimpleModule::SimpleClass STATIC hello world"
    end

  end

end

# non-namespaced extend example : STATIC : module method addition to class
class SimpleClassExtend
  extend SimpleModule
end

# non-namespaced include example : NON-STATIC : module method addition to class
class SimpleClassInclude
  include SimpleModule
end

# inheritance example
class SimpleClassInherit < SimpleModule::SimpleModuleClass

  # method override with call to parent
  def class_scoped_non_static_put
    super
    puts '> I have something extra to say!'
  end

end

# code only executed if this was the file run (java main equivalent)
if __FILE__ == $0

  # module function call examples
  SimpleModule.module_function_put
  SimpleModule.module_scoped_put_two

  # module method called statically on a class
  SimpleClassExtend.module_scoped_put 'statically'

  # module method called non-statically on a class
  @included = SimpleClassInclude.new
  @included.module_scoped_put 'non-statically'

  # namespace call of class static method
  SimpleModule::SimpleModuleClass.class_scoped_static_put

  # quick & dirty call of a module non-function method:
  Class.new.extend(SimpleModule).module_scoped_put 'statically on-the-fly'

  # inheritance call : static
  SimpleClassInherit.class_scoped_static_put

  # inheritance call : non-static
  SimpleClassInherit.new.class_scoped_non_static_put
end

# code always run if this class is loaded/required/run
puts 'hi there I always display when loaded/required/run'

1 comment:

  1. Thanks! Interesting to hear your thoughts on Ruby vs. Java.

    ReplyDelete