This is my first article in http://railsmagazine.com, it was published in issue 3, so basically I’m just republishing it here again.
Observer and singleton are two common design patterns that a programmer should be familiar with, however what made me write about them, is that both are there out of the box for you to use in ruby.
So let’s have a look at both and see how ruby help you use them directly:
According to Wikipedia:
The observer pattern (sometimes known as publish/subscribe) is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems.
So how does ruby help you implementing this design pattern? Well, the answer is by mixing the observable module into your subject (observed object).
Let’s take an example, let’s suppose we have a banking mechanism that notifies the user by several ways upon withdrawal operations that leaves the account with balance less or equal to $0.5.
If we look deeply at this problem, we can qualify it as a good candidate for observer design pattern, where the bank account is our subject and the notification system as the observer.
Here is a code snippet for this problem and it’s solution:
# require the observer lib file require “observer” class Notifier end class EmailNotifier < Notifier def update(bank_account) if bank_account.balance <= 10 puts “Sending email to: ‘#{bank_account.owner}’ informing him with his account balance: #{bank_account.balance}$.” # send the email mechanism end end end class SMSNotifier < Notifier def update(bank_account) if bank_account.balance <= 0.5 puts “Sending SMS to: ‘#{bank_account.owner}’ informing him with his account balance: #{bank_account.balance}$.” # send sms mechanism end end end class BankAccount # include the observable module include Observable attr_reader :owner,:balance def initialize(owner,amount) @owner,@balance = owner,amount # adding list of observes to the account add_observer EmailNotifier.new add_observer SMSNotifier.new end # withdraw operation def withdraw(amount) # do whatever you need @balance -=amount if (@balance - amount) > 0 # now here comes our logic # issue that the account has changed changed # notify the observers notify_observers self end end account = BankAccount.new “Jim Oslen”, 100 account.withdraw 99.5 #=>Sending email to: ‘Jim Oslen’ informing him with his account balance: 0.5$. #=>Sending SMS to: ‘Jim Oslen’ informing him with his account balance: 0.5$.
So to user ruby observer lib we have to implement four things:
1- Require the ‘observer’ lib and include it inside the subject (observed) class.
2- Declare the object to be ‘changed’ and then notify the observers when needed – just like we did in ‘withdraw’ method.
3- Declare all needed observers objects that will observe the subject.
4- Each observer must implement an ‘update’ method that will be called by the subject.
You can find observers in rails when using ActiveRecord, it’s a way to take out all ActiveRecord callbacks out of the model, for example a one would do this:
class User < ActiveRecord::Base after_create :send_email private def send_email #send a welcome email end end
A neater solution is to use Observers:
class UserObserver < ActiveRecord::Observer def after_create(user) #send a welcome email end end
You can generate the previous observer using the following command:
ruby script/generate observer UserYou still can have observers that map to models that don’t match with the observer name using the ‘observe’ class method, you also can observe multiple models using the same method:
class NotificationObserver < ActiveRecord::Observer observe :user, :post def after_create(record) #send thanks email end end
Finally don’t forget to add the following line inside config/environment.rb to define observers:
config.active_record.observers = :user_observer
According to Wikipedia:
In software engineering, the singleton pattern is a design pattern that is used to restrict instantiation of a class to one object. (This concept is also sometimes generalized to restrict the instance to a specific number of objects – for example, we can restrict the number of instances to five objects.) This is useful when exactly one object is needed to coordinate actions across the system.
The singleton design pattern is used to have one instance of some class, typically there are many places where you might want to do so, just like having one database connection, one LDAP connection, one logger instance or even one configuration object for your application.
In ruby you can use the singleton module to have the job done for you, let’s take ‘application configuration’ as an example and check how we can use ruby to do the job:
# require singleton lib require ‘singleton’ class AppConfig # mixin the singleton module include Singleton # do the actual app configuration def load_config(file) # do your work here puts “Application configuration file was loaded from file: #{file}” end end conf1 = AppConfig.instance conf1.load_config “/home/khelll/conf.yml” #=>Application configuration file was loaded from file: /home/khelll/conf.yml conf2 = AppConfig.instance puts conf1 == conf2 #=>true # notice the following 2 lines won’t work # new method is private AppConfig.new rescue(puts $!) #=>private method `new’ called for AppConfig:Class # dup won’t work conf1.dup rescue(puts $!) #=>can’t dup instance of singleton AppConfig
So what does ruby do when you include the singleton method inside your class?
1- It makes the ‘new’ method private and so you can’t use it.
2- It adds a class method called instance that instantiates only one instance of the class.
So to use ruby singleton module you need two things:
1- Require the lib ‘singleton’ then include it inside the desired
class.
2- Use the ‘instance’ method to get the instance you need.
Twitter Trackbacks for Observer and Singleton design patterns in Ruby - Khaled alHabache’s official blog [khelll.com] on Topsy.com | August 22nd, 2009 at 5:30 pm #
[...] Observer and Singleton design patterns in Ruby – Khaled alHabache’s official blog http://www.khelll.com/blog/ruby/observer-and-singleton-design-patterns-in-ruby – view page – cached #Khaled alHabache’s official blog RSS Feed Khaled alHabache's official blog » Observer and Singleton design patterns in Ruby Comments Feed Khaled alHabache's official blog Ruby introspection Ruby 1.9 Encoding Fun — From the page [...]
gui_maranhao | March 1st, 2010 at 5:40 am #
Hello,
I intend to create an Observes to observe some models that dispatch specific actions. Depends on the current model, one of the actions is dispatched. To know which action must be dispatched, many business rules should be certified, making my Observer very complex.
Do you recommend to create this kind of Observer?
thanks, guilherme maranhão
khelll | March 1st, 2010 at 8:41 am #
@gui_maranhao, Rails Observers don’t work with controller actions, instead with model callbacks. Anyway I prefer observers for callbacks to normal model methods, specially when the code base becomes complex.
gui_maranhao | March 1st, 2010 at 8:55 am #
Thanks,
When I mentioned “actions” I did not mean controller’s actions, I meant inputs in my db to register loggs and things like that, sorry.
I also use them as callbacks to model methods. I’ve answered my question!
thanks again
gui_maranhao | March 1st, 2010 at 9:01 am #
In my last post I wrote: “I’ve answered my question!”. SORRY, it should have been: “YOU’ve answered my question!”
khelll | March 1st, 2010 at 9:02 am #
No worries, always try to make your code readable by breaking it to parts that makes sense. Observers are cool place for callbacks and complex code related to them.
gui_maranhao | March 3rd, 2010 at 10:00 am #
Khelll,
In order to reduce the complexity of my observers, I would like to know if it is possible for my Observer to observe specific methods (not just the models) of my models, like this:
def after_
…
end
Thanks, guilherme
gui_maranhao | March 3rd, 2010 at 10:07 am #
in my last post, the method’s name was:
def after_something
…
end
thanks
Evgeniy Dolzhenko | March 3rd, 2010 at 11:36 pm #
I really dig your blog, and the “cool Ruby questions” series specifically.
Sorry if that sounds like total dumbass nitpicking, but a few whitespaces in the first code sample won’t hurt IMO:
@owner, @balance = owner, amount
...
@balance -= amount if (@balance - amount) > 0