Debugging and RDebug
When I first started working in Rails, I went with TextMate on my Mac. I really like TextMate, but trying to debug problems was really, really painful and time consuming at first.
A few tips would have gone a long way:
- I really did not understand how to use “script/console” to debug problems; now, I use it all the time.
- I did not know how to use “yell”; instead, I spent a lot of time searching for statements in really large log files.
- I did not know how to leverage the console to see what was happening and which controllers were getting invoked. Once I understood these things, debugging got to be a lot easier.
Now, I really do not miss having a debugger. As someone who has used Eclipse and similar environments for the last 15 years, I find this shocking! I did a trial of Aptana, and after that was unable to re-install the non-professional version. I now have Rubymine, but I rarely need debugging help.
Tonight, however, I needed a debugger to be more productive. Instead of firing up RubyMine, I decided to give RDebug a try. Though it is limited as a command-line debugger, I am very impressed.
I simply added require ‘ruby-debug’ to the top of my file. For my breakpoint, I added the statement debugger, and then executed the program. When it hit that line, it dropped into a command-line debugger.
There are a number of commands, but the following took care of what I needed:
- (p)rint. Prints out variables: p foo.id
- (n)ext. Kind of like step over
- (s)tep. Kind of like step into
- (c)ontinue. Self explanatory
- (e)valuate. Self explanatory
At least 80% of the time, you won’t need a debugger. When you do, the above should cover what you need for 80% of that. Which means you should rarely need a more powerful debugger than ruby-debug.
Properly Moving Validation into Models
When you first start to learn Rails, you read about Model Validation, which consists of statements such as “validates_numericality_of :the_field”. This prevents non-conforming models from being saved. Properly leveraged, these help ensure business constraints with an iron fist (excepting validation override).
Where to Best Enforce Business Constraints
Web apps constantly process form inputs and do the following:
- Ensure inputs conform to business constraints (e.g., is the value only 1 to 60)
- Give the user feedback on non-conforming inputs to allow changes.
This validation is typically done at one of the following three levels:
- Controllers. This is the last place validation should be done, but it is often the most tempting. Keep controllers as lean as possible and place business logic in models to conform to MVC. If your system exposes an API, controller business logic will be missed or duplicated.
- Method within Model. This is an improvement. Controllers can invoke this method to ensure business constraints prior to the models being saved. Furthermore, the logic remains in the model. However, if the controller forgets to invoke this method, then the logic is missed.
- Model Validation Statement or Validation Method. This is the most iron-clad way to enforce system constraints. If they do not conform, they simply cannot be saved (unless you use the Rails option to ignore validation, of course).
In addition, the testing of the model business logic can be handled in the unit as opposed to functional tests.
Potential Problems
When first moving existing validation logic into the third option, you should expect to spend some time updating previously less strict test data.
Sometimes you will run into problems with what I call phantom models, as follows:
- In response to multi-step user input, a blank phantom model instance is first created.
- After all appropriate inputs are received, this phantom model is updated.
- You later add validation(s) to the model.
- Step 1 no longer works because a blank model does not conform to the new validation.
- Thus, you need to either refactor, remove the validations, or add the blank model without validation being enforced (with the risks of invalid data in the database).
The control Rails gives you over generated error messages using declarations is also not ideal. Consider the following for a field named minutes_running:
:message => “must be a whole number between 0 and 60″
This will generate the following message:
“Minutes Running must be a whole number between 0 and 60″
Want a different message that does not begin with the database field name? I would love to know how.
Declarative Constraints
What I like best about doing this at the model level is the declarative approach. You simply declare the constraints, which is simpler, faster than coding, and clearer later on to you and others. If not powerful enough, you can also write validation methods and declare them as being part of model validation.
One mistake I made was not realizing how flexible and configurable these declarative statements are. The following (trimmed) sample from a single model ends this article:
validates_presence_of :user_name
validates_presence_of :run_count,
:if => :can_process, # boolean entity in model, could also use boolean method
:message => "must be specified if this can be processed"
validates_numericality_of :minutes_exercising,
:greater_than => -1,
:less_than => 61,
:only_integer => true, # no floating points allowed!
:message => "must be a whole number between 0 and 60"
validate :some_method_with_complex_validation?
Access Modifiers – Weaker API and Security Support in Ruby?
When first coding in Java in 1996, it took me a while to appreciate the newest access modifier – default or package. Then the norm was exposing almost everything as public, including fields, unless there was a good reason to restrict. Joshua Bloch’s Effective Java later reversed that, utilizing access modifiers appropriately to enhance security and scope API’s.
Default vs. public access was particularly important. A component of functionality (i.e., Java package) allowed all the containing classes to access the equivalent of their public methods to accomplish its responsibility solely within the package. However, it could prevent access to all these from the outside world and limit clients strictly to an API. This API was designated by applying the public access modifier to a limited subset of these classes and methods. This provided a number of benefits:
- Changing the underlying implementation without backward compatibility concerns, including consumers coupling their code to an internal implementation detail. Not because consumers don’t use restricted items, but because they are unable to access them
- Explicit API classes and methods which are more easily JavaDoc’d
- Restricting sub-classing only within the package
- Enhanced security
This was a powerful encapsulation capability, and one that seems to be sorely missing in Ruby with its limited three access modifiers: public, protected, and private.
Access Modifiers
Ruby protected and private modifiers are radically different than Java.
Private
Like Java, a private method can only be invoked within the methods defined in the class it is declared in.
In Ruby, however, private access is less restrictive. The methods can also be accessed by any sub-class, not just the class defined within. In addition, the private methods can be overridden in sub-classes. This means that I cannot enforce the functionality of any method for any class remaining the same; it can always be altered by sub-classes, circumventing my attempts to enforce invariants in my system. In addition, all functionality can be directly accessed (and coupled) by clients via sub-classing. Effectively, all functionality can be directly accessed and altered in Ruby, and there is nothing you can do to prevent it.
Protected
Protected methods can invoked and overridden by sub-classes. Like private methods, protected methods can only be invoked within the methods defined in the class it is declared in. However, they can also be invoked on other instances of the same class or sub-class only. Note that public methods fully lend themselves to being overridden as well.
Member Fields Automatically Private
One thing I do like about Ruby is that member fields are automatically private; the accessors are used to expose the equivalent of getters and setters.
One thing I am not wild about is how the methods always default to public.
Final
I am still trying to find something that is equivalent to Java’s final keyword in Ruby. Not only do I not see a way to lock down the behavior of public methods in sub-classes, even private and protected methods can be overridden with no ability to limit this.
Package Level Containment
I do not see a Ruby construct equivalent to the Java package. That is, something that can contain other classes and limit what is exposed outside of it.
I tried an experiment nesting sub-classes within another class, treating that class as a package. By placing some of the sub-classes beneath the private keyword of the containing class, I had hoped that classes could not be directly accessed and instantiated outside of the containing class. Unfortunately, this does not work. Furthermore, I see no mechanism to apply access modifiers to classes.
In my opinion, too much is exposed in Ruby, but perhaps as I learn more about the Ruby and Rails philosophy I will better understand why.
Lab Code
The following is the quick and dirty code I threw together to experiment with the access modifier behavior:
class A def a_public_method_invoking_private a_private_method end def a_public_method_invoking_protected a_protected_method end def a_public_method_invoking_other_protected a_other = A.new a_other.a_protected_method end def a_public_method_invoking_other_private a_other = A.new a_other.a_private_method end def a_public_method puts "a_public_method_invoked" end protected def a_protected_method puts "a_protected_method_invoked" end private def a_private_method puts "a_private_method_invoked" end end class B < A def a_public_method_invoking_parent_private_method puts "surprisingly, your can invoke superclass private methods." a_private_method end def a_public_method_invoking_parent_protected_method puts "You can invoke superclass protected methods." a_protected_method end def a_public_method_invoking_other_protected_in_superclass a_other = A.new a_other.a_protected_method end end class C < A public :a_public_method protected :a_protected_method private :a_private_method def a_public_method super puts "a_public_method_invoked - subclass C - allowed for public" end def a_protected_method super puts "a_protected_method - overridden extra behavior in C" end def a_private_method super puts "a_private_method - overridden extra behavior in C" end end class FooPackage class PublicClass def public_method puts "PublicClass public_method invoked. About to invoke own private method, then public method of private class." private_method p = PrivateClass.new p.public_method end private def private_method puts "PublicClass private_method invoked" end end private class PrivateClass def public_method puts "PrivateClass public_method invoked. About to invoke own private method." private_method end private def private_method puts "PrivateClass private_method invoked" end end end a1 = A.new #a.a_private_method - this is not legal #a1.a_public_method_invoking_private - this is legal #a1.a_public_method_invoking_protected - this is legal #a1.a_public_method_invoking_other_protected - this is legal #a1.a_public_method_invoking_other_private - this is not legal b1 = B.new #b1.a_public_method_invoking_private - this is legal #b1.a_public_method_invoking_parent_private_method - this is legal #b1.a_public_method_invoking_parent_protected_method - this is legal b1.a_public_method_invoking_other_protected_in_superclass c1 = C.new #c1.a_public_method_invoking_protected - this is legal #c1.a_public_method_invoking_private - this is legal #c1.a_public_method - this is legal fp = FooPackage::PublicClass.new #fp.public_method - this is legal and works its way through fprivate = FooPackage::PrivateClass.new # this is legal, despite my hopes it would not be fprivate.public_method
What Happened to Fail Fast and Early?
When I first started developing right after college in C++ and came across an error, I used to follow the common pattern of trying to take what seemed like the best action to avoid crashing. Later, I learned about the concept of “Fail Fast and Early” – where if an error exists, you want to fail and identify it ASAP. That way, you can correct it early on instead of just masking it.
Surprisingly to me, Rails seems to adopt the former approach.
You want to create a date with invalid input? No problem, we’ll just set it to today and never complain. Want to convert the string “abc” to be an integer? No problem, we’ll just set it to zero and not a peep from us. You want to update an ActiveRecord integer field to a float? No worries, we’ll just silently drop the decimal portion.
Of course, sometimes (often, actually), you want to inform the user when they have supplied invalid input to take corrective action.
So, if the date is set to today or the integer has a value of 0, then you know you have a problem. Except that these are also valid values. Surely there must be a method on these things you can invoke to see if their string representations are valid, right? Er, I mean they must be, somewhere, because of course it would exist, right?
I have a string, and I want to see if this string is an integer or float. Surely, there is a method I can use. There is, as long as you write it yourself as I did via regex.
If Rails is going to auto-convert erroneous inputs, it could at least provide a mechanism to detect invalid inputs.
In Java, if you try to create an Integer from the string “abc”, it will immediately fail with an exception so you a) know what is wrong, and b) have a good mechanism for recovering. In Ruby, I can create a date from “3/23/sdes” without complaint (hint – it resolves to 3/23 of this year).
I am sure there is Ruby/Rails philosophy which drives this approach, but I just don’t know what it is yet. For the moment, it is making UI-based validation a lot more difficult than it should be.