Friday, April 27, 2007

Ruby: Validatable 1.2.2 released

The Validatable gem received a fair amount of attention in the past few weeks. Thanks to Ali Aghareza, Jason Miller, Xavier Shay, and Anonymous Z for their contributions.

Validation Groups
On my previous project we found that our object.valid? method needed to depend on the role that an object was currently playing. This led to the introduction of groups.

Validation groups can be used to validate an object when it can be valid in various states. For example a mortgage application may be valid for saving (saving a partial application), but that same mortgage application would not be valid for underwriting. In our example a application can be saved as long as a Social Security Number is present; however, an application can not be underwritten unless the name attribute contains a value.
  class MortgageApplication
include Validatable
validates_presence_of :ssn, :groups => [:saving, :underwriting]
validates_presence_of :name, :groups => :underwriting
attr_accessor :name, :ssn
end

application = MortgageApplication.new
application.ssn = 377990118
application.valid_for_saving? #=> true
application.valid_for_underwriting? #=> false
As you can see, you can use an array if the validation needs to be part of various groups. However, if the validation only applies to one group you can simply use a symbol for the group name.

The inspiration for adding this functionality came from the ContextualValidation entry by Martin Fowler.

validates_true_for
The validates_true_for method was added to allow for custom validations.

The validates_true_for method can be used to specify a proc, and add an error unless the evaluation of that proc returns true.
  class Person
include Validatable
validates_true_for :first_name, :logic => lambda { first_name == 'Book' }
attr_accessor :first_name
end

person = Person.new
person.valid? #=> false
person.first_name = 'Book'
person.valid? #=> true
The logic option is required.

validates_numericality_of
This release also adds the validates_numericality_of method. The validates_numericality_of method takes all of the standard parameters that the other validations take: message, times, level, if, group.

Validates that the specified attribute is numeric.
  class Person
include Validatable
validates_numericality_of :age
end
after_validate hook method
Another new feature of this release is the after_validate hook. This feature allows you to manipulate the instance after a validation has been run. For example, perhaps you are happy with the default messages; however, you also want the attribute to be appended to the message. The following code uses that example and shows how the after_validate hook can be used to achieve the desired behavior.
class Person
include Validatable
validates_presence_of :name
attr_accessor :name
end

class ValidatesPresenceOf
after_validate do |result, instance, attribute|
instance.errors.add("#{attribute} can't be blank") unless result
end
end

person = Person.new
person.valid? #=> false
person.errors.on(:name) #=> "name can't be blank"
The after_validate hook yields the result of the validation being run, the instance the validation was run on, and the attribute that was validated.

include_validations_for takes options
The include_validations_for method was changed to accept options. The currently supported options for include_validations_for are :map and :if.
  class Person
include Validatable
validates_presence_of :name
attr_accessor :name
end

class PersonPresenter
include Validatable
include_validations_for :person, :map => { :name => :namen },
:if => lambda { not person.nil? }
attr_accessor :person

def initialize(person)
@person = person
end
end

presenter = PersonPresenter.new(Person.new)
presenter.valid? #=> false
presenter.errors.on(:namen) #=> "can't be blank"
The person attribute will be validated. If person is invalid the errors will be added to the PersonPresenter errors collection. The :map option is used to map errors on attributes of person to attributes of PersonPresenter. Also, the :if option ensures that the person attribute will only be validated if it is not nil.

validates_confirmation_of
The validates_confirmation_of method now takes the :case_sensitive option. If :case_sensitive is set to false, the confirmation will validate the strings based a case insensitive comparison.

validates_length_of
The validates_length_of method now takes the :is option. If the :is option is specified, the length will be required to be equal to the value given to :is.

Bugs
The comparison in validates_format_of was changed to call to_s on the object before execution. The new version allows you to validate the format of any object that implements to_s.

Thursday, April 26, 2007

DSL: Expressive Software

Lately I've been thinking about DSLs that are designed for and by programmers. After some recent work and discussions I formed the following idea.

Expressive Software is a style of software development inspired by Domain Specific Languages. In-fact, Expressive Software can be considered a type of Domain Specific Language. Specifically, Domain Specific Languages designed for programmers can be categorized as Expressive Software.

The term Domain Specific Language is overloaded and suffers from lack of clarity*. Because of the lack of clarity some consider Rails to be a Domain Specific Language and others are vehemently against the idea. However, it's hard to argue against Rails being considered expressive software.

The expressiveness of Rails can be seen in it's many class methods. A good example of a class method designed to be expressive is the has_many method of ActiveRecord::Base. The has_many method and all the other expressive methods of Rails make working with it easy when developing new and maintaining old code.

An example of creating your own Expressive Software could be a class that defines a workflow path. The class could do this via the following syntax:
class StandardPath
pages :select_product, :contact_information, :summary

path select_product >> contact_information >> summary
end
The same application may have a path for existing users
class ExistingUserPath < StandardPath
path select_product >> summary
end
The above example utilizes an expressive class method, similar to Rails. In addition it also shows an example of using a Fluent Interface (the path method). Utilizing a Fluent Interface helps create software that expresses intent within the code. This is one of the goals of Expressive Software, to easily convey the intent within the code itself.

Software designed with an Expressive Software mentality is easier to read and maintain. This style of software development can lead to productivity gains. While it's hard to quantify the level of productivity gains, Rails (and various other examples such as Rake and Builder) prove that a higher level of productivity can and should be achieved.

* This isn't the first time I've mentioned that I believe "DSL" is too vague. In the Business Natural Language material I've also discussed why I think there's a need to create more descriptive phrases for more focused ideas.

Friday, April 20, 2007

Ruby: Class Methods

It's very common when moving from Java or C# to be weary of Ruby's class methods. Many people have written about how static methods are evil in C# and Java, but do those arguments apply to Ruby?

Complaints
  1. Static methods cannot participate in an Interface. Ruby: No interfaces, thus irrelevant.
  2. Static methods cannot be abstract. Ruby: No concept of abstract, also irrelevant.
  3. Static methods are not polymorphic. Ruby: doesn't seem like it applies since you don't declare type in Ruby. Also, you don't access class methods directly from an instance.
  4. Static methods are hard to test. Ruby: Class methods are as easy to test and mock as any instance method. Perhaps this is because they are simply instance methods of the sington class.


At this point I ran out of complaints, but it's been over a year since I've touched C#. If you have complaints of your own that support or oppose this entry, please drop them in the comments.

Another thought about static methods is that they tend not to encourage proper object design. For example, a method that removes characters from a string is probably best served on the string itself.
class String
# an example class method
def self.delete(string, characters)
...
end

# a better instance method
def delete(characters)
...
end
end
You see this type of class method much less in Ruby since Ruby has open classes. But, it's good to remember to put methods on the object whose data is being manipulated when possible.

So what exactly is a class method?
class Parser
def self.process(script)
# ...
end
end

Parser.singleton_methods.inspect #=> ["process"]
From the example, you could say a class method is a singleton_method. Where do singleton methods live?

class Parser
def self.process(script)
# ...
end

def self.singleton
class << self; self; end
end
end

Parser.singleton.instance_methods(false).inspect
#=> ["singleton", "new", "superclass", "allocate", "process"]
Singleton (class) methods are actually all instance methods of the singleton class.

In fact, you can define a class method various ways, but the result is always an instance method on the singleton class.
class Parser
def self.self_process(script)
# ...
end

def Parser.parser_process(script)
# ...
end

class << self
def singleton_process(script)
# ...
end
end

def self.singleton
class << self; self; end
end
end

Parser.singleton.instance_methods(false).inspect
#=> ["singleton_process", "parser_process", "new",
"superclass", "allocate", "singleton", "self_process"]
Of course, the process method is also inherited by subclasses and the singleton classes of subclasses.
class Parser
def self.process(script)
# ...
end
end

class Object
def self.singleton
class << self; self; end
end
end

class HtmlParser < Parser

end

HtmlParser.singleton_methods.inspect
#=> ["singleton", "process"]
HtmlParser.singleton.instance_methods(false).inspect
#=> ["singleton", "new", "superclass", "allocate", "process"]
In a later post I'll talk about where those methods are stored in the underlying C code.

Thanks to James Lewis, Matt Deiters, and Mike Ward for help on this entry.

Thursday, April 19, 2007

Ruby: Assigning instance variables in a constructor

Every time I assign an instance variable in a constructor I remember that I've been meaning to write something that takes care of it for me.
class DomainObject
attr_r