How to send email asynchronously using Devise and Rails3
Hello everyone.
I’d show a workaround to send email asynchronously using Devise and Rails3.
Suppose we have already up and running our application with Devise and delayed_job correctly installed.
A first attempt was to add in config/initializers the following file (devise_async.rb):
module Devise
module Models
module Confirmable
handle_asynchronously :send_confirmation_instructions
end
module Recoverable
handle_asynchronously :send_reset_password_instructions
end
module Lockable
handle_asynchronously :send_unlock_instructions
end
end
end
This workaround has worked in part: the send method has been properly enqueued in the database, but when delayed_job tries to fire the job, the following error is raised:
[Worker(host:stefano-desktop pid:13153)]
As you can see, the job is trying to call the wrong send method: send_confirmation_instructions_without_delay.
At this point, I’ve implemented an even more dirty hack, overriding Devise’e methods using the syntax specified by intridea to send emails in the background:
module Devise
module Models
module Confirmable
# Send confirmation instructions by email
def send_confirmation_instructions
generate_confirmation_token! if self.confirmation_token.nil?
::Devise.mailer.delay.confirmation_instructions(self)
end
end
module Recoverable
# Resets reset password token and send reset password instructions by email
def send_reset_password_instructions
generate_reset_password_token!
::Devise.mailer.delay.reset_password_instructions(self)
end
end
module Lockable
# Send unlock instructions by email
def send_unlock_instructions
::Devise.mailer.delay.unlock_instructions(self)
end
end
end
end
This solution, however, is too tied to the implementation of Devise and is therefore not a good one (besides being really really dirty).
The latest idea, which represents the solution I’ve used is implemented as follows: use the alias_method in this way:
module Devise
module Models
module Confirmable
alias_method :send_confirmation_instructions_without_delay, :send_confirmation_instructions
handle_asynchronously :send_confirmation_instructions
end
module Recoverable
alias_method :send_reset_password_instructions_without_delay, :send_reset_password_instructions
handle_asynchronously :send_reset_password_instructions
end
module Lockable
alias_method :send_unlock_instructions_without_delay, :send_unlock_instructions
handle_asynchronously :send_unlock_instructions
end
end
end
This latest hack works a treat; is not the best but let you send mail with Devise asynchronously.
If you have any better solutions, do not hesitate to share!
How to implement two dropdowns dependent on each other using Django and jQuery
Hello everyone.
With today’s article I want to show how to implement two dropdowns dependent on each other using Django and jQuery.
Suppose we have a 1-N relationship between car brands and car models: we want to show the list of brands in the first dropdown; then once you have selected a brand, the second dropdown will show a list of filtered models for that brand.
Suppose we have our models defined as follows:
class VehicleBrand(models.Model):
description = models.CharField(max_length=100)
code = models.SlugField(primary_key=True)
class VehicleModel(models.Model):
description = models.CharField(max_length=100)
code = models.SlugField(primary_key=True)
brand = models.ForeignKey(VehicleBrand)
Let’s write a templatetag that will be included in our templates:
from models import VehicleBrand
from django import template
register = template.Library()
@register.inclusion_tag("brand_model_select.html")
def brand_model_select():
brand_list = VehicleBrand.objects.all()
return {'brand_list' : brand_list}
As you can see, the templatetag requires a support html template, defined as follow:
<form action="" method="get" accept-charset="utf-8">
<select name="brand" id="brand">
<option value="Z">Select a brand</option>
{% for brand in brand_list %}
<option value="{{ brand.code}}">{{ brand.description }}</option>
{% endfor %}
</select>
<select name="model" id="model" disabled="true">
<option>Select a model</option>
</select>
</form>
<script>
$(document).ready(
function() {
$("select#brand").change(function() {
if ($(this).val() == 'Z') {
$("select#model").html("<option>Select a model</option>");
$("select#model").attr('disabled', true);
}
else {
var url = "/brand/" + $(this).val() + "/all_json_models";
var brand = $(this).val();
$.getJSON(url, function(models) {
var options = '<option value="Z">Select a model</option>';
for (var i = 0; i < models.length; i++) {
options += '<option value="' + models[i].pk + '">' + models[i].fields['description'] + '</option>';
}
$("select#model").html(options);
$("select#model option:first").attr('selected', 'selected');
$("select#model").attr('disabled', false);
});
}
});
$("select#model").change(function(vent) {
if ($(this).val() == -1) {
return;
}
myAwesomeFunctionToCallWhenAModelIsSelected();
});
});
}
</script>
Please note that once a brand is selected, the following url will be invoked via Ajax request:
/brand/”selected_brand_code”/all_json_models
then we need to define a view to handle this request:
def all_json_models(request, brand):
current_brand = VehicleBrand.objects.get(code=brand)
models = VehicleModel.objects.all().filter(brand=current_brand)
json_models = serializers.serialize("json", models)
return HttpResponse(json_models, mimetype="application/javascript")
and in urls.py:
...
(r'^brand/(?P<brand>[-\w]+)/all_json_models/$', 'all_json_models'),
Finally, the javascript method myAwesomeFunctionToCallWhenAModelIsSelected() will be invoked when a model will be selected.
How to create a custom feed in Django using the Syndication Feed Framework
Hello everyone.
In this article I will show you how to create a custom feed in Django, using the Django Syndication Feed Framework 1.2 or higher.
I’ve had to create an XML with some more tags for each item in the feed.
In particular, in addition to the usual tags present in an RSS feed, I’ve added the
<short_description/>
and
<image/>
tags.
Reading the official documentation of the framework, it is recommended to create a custom feed generator, but the process is not very clear.
Let’s see step by step how to fix everything.
First we create a custom feed generator.
This generator must create the same XML that would produce the framework’s default generator and for each item in the list it will add the new tags.
def add_item_elements(self, handler, item):
super(CustomFeedGenerator, self).add_item_elements(handler, item)
handler.addQuickElement(u"image", item['image'])
handler.addQuickElement(u"short_description", item['short_description'])
We see that the generator calls the method add_item_elements(handler, item) of its superclass and then adds new XML tags for each item.
Note that the values that will be inserted in the image and short_description tags must be present in the item dictionary passed as argument.
So let’s create our CustomFeed class that inherits from Feed class and see how to tell it to use the CustomFeedGenerator and how to insert the new values in the item dictionary.
feed_type = CustomFeedGenerator
def title(self, obj):
return u"My amazing feed"
def description(self, obj):
return u"My feed description"
def link(self, obj):
return "http://%s" % obj.get_absolute_url()
def item_extra_kwargs(self, obj):
"""
Returns an extra keyword arguments dictionary that is used with
the `add_item` call of the feed generator.
Add the 'content' field of the 'Entry' item, to be used by the custom feed generator.
"""
return { 'image': obj.get_picture().get_medium_url() if obj.get_picture() else "",
'short_description' : obj.get_preview(),}
CustomFeed is very similar to the various feeds that we are accustomed. The only difference is represented by the statement of the type of feed
feed_type = CustomFeedGenerator
and by the method
item_extra_kwargs
able to append any new values in the item dictionary, obtaining the desired result.
How to install PostgreSQL and psycopg2 on Osx Snow Leopard
Hello everyone.
In my last post I showed how to install MySQL from source on Osx.
Among the many comments received, some of them suggested me to use homebrew
I recommend everyone to use this tool in order to easily install several unix packages on OSX.
Among the packages that can be installed there’s also PostgreSQL, the subject of today’s post.
If you install PostgreSQL via homebrew, maybe have a look at this post
This post is instead directed to those who are willing to install the database from source in /usr/local/postgresql-8.4.4
Step 1: Set the $PATH environment variable
Open a terminal and set the $PATH environment variable in order to link the correct folders in /usr/local/
Add, if it does not exist, this line at the bottom of the .profile file:
and reload the $PATH in this way:
To verify that our $PATH contains the paths set above, type the following command:
Step 2: Download PostgreSQL
Create a new folder to download the sources and compile them:
cd ~/src
Download the latest version available at the time of writing this tutorial:
Step 3: Compile and Install
Build and install PostgreSQL with the following commands:
rm postgresql-8.4.4.tar.gz
cd postgresql-8.4.4
./configure --prefix=/usr/local/postgresql-8.4.4
ARCH=x86_64 CFLAGS="-arch x86_64" LDFLAGS="-arch x86_64" make
make install
Create a symbilic link, used before in the $PATH
mkdir /usr/local/pgsql/data/
Create now a postgres user, owner of the server:
dscl localhost create /Local/Default/Users/postgres PrimaryGroupID 0
dscl localhost create /Local/Default/Users/postgres UniqueID 75
dscl localhost create /Local/Default/Users/postgres UserShell /bin/bash
dscl localhost passwd /Local/Default/Users/postgres
dscl localhost create /Local/Default/Users/postgres NFSHomeDirectory /var/home/postgres
mkdir -p /var/home/postgres
chown -Rf postgres:postgres /var/home/postgres
dscl localhost create /Local/Default/Groups/postgres
dscl localhost create /Local/Default/Groups/postgres UniqueID 75
dscl localhost append /Local/Default/Groups/postgres GroupMembership postgres
And let’s give him read permissions over the installation directory:
In a new console, log in as postgres and start the server:
/usr/local/pgsql/bin/initdb -E UTF8 -D /usr/local/pgsql/data/
/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data/ -l /usr/local/pgsql/data/postgresql.log start
If the server is started correctly, we can create our test database:
psql test
Now delete unnecessary folders created when creating the user:
rm -rf /var/home
dscl localhost delete /Local/Default/Users/postgres NFSHomeDirectory
dscl localhost passwd /Local/Default/Users/postgres
exit
Then we can create scripts to start and stop the PostgreSQL server using the command line.
We create a bin folder in our home and the following file, also giving execute permissions:
touch pgsqlscript
chmod +x pgsqlscript
Edit the file you’ve just created by inserting the following body:
start()
{
echo -n "Starting PostgreSQL server"
sudo su postgres -c '/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data/ -l /usr/local/pgsql/data/postgresql.log start'
return
}
stop()
{
echo -n "Stopping PostgreSQL server"
sudo su postgres -c '/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data/ stop'
return
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "Usage: {start|stop|restart}"
exit 1
;;
esac
exit $?
At this point you can start the server by typing “pgsqlscript start” in the console. To stop the server just type “pgsqlscript stop”.
Step 4: Install Ruby PostgreSQL drivers
Just install the pg gem to have access to the database.
Step 5: Install Python drivers for PostgreSQL
Download the psycopg2 package
curl -O http://initd.org/psycopg/tarballs/psycopg2-2.2.2.tar.gz
tar xzfv psycopg2-2.2.2.tar.gz
cd psycopg2-2.2.2
and modify the setup.cfg file with this one:
define=PSYCOPG_EXTENSIONS,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
# PSYCOPG_EXTENSIONS enables extensions to PEP-249 (you really want this)
# PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower)
# HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4
# HAVE_PQPROTOCOL3 should be defined on PostgreSQL >= 7.4
# PSYCOPG_DEBUG can be added to enable verbose debug information
# PSYCOPG_OWN_QUOTING can be added, but it is deprecated (will go away in 2.1)
# PSYCOPG_NEW_BOOLEAN to format booleans as true/false vs 't'/'f'
# Set to 1 to use Python datatime objects for default date/time representation.
use_pydatetime=1
# If the build system does not find the mx.DateTime headers, try
# uncommenting the following line and setting its value to the right path.
#mx_include_dir=
# For Windows only:
# Set to 1 if the PostgreSQL library was built with OpenSSL.
# Required to link in OpenSSL libraries and dependencies.
have_ssl=0
# Statically link against the postgresql client library.
static_libpq=0
# "pg_config" is the preferred method to locate PostgreSQL headers and
# libraries needed to build psycopg2. If pg_config is not in the path or
# is installed under a different name uncomment the following option and
# set it to the pg_config full path.
pg_config=/usr/local/pgsql/bin/pg_config
# If "pg_config" is not available, "include_dirs" can be used to locate
# postgresql headers and libraries. Some extra checks on sys.platform will
# still be done in setup.py.
# The next line is the default as used on psycopg author Debian laptop:
#include_dirs=/usr/local/lib
# Uncomment next line on Mandrake 10.x (and comment previous ones):
#include_dirs=/usr/include/pgsql/8.0:/usr/include/pgsql/8.0/server
# Uncomment next line on SUSE 9.3 (and comment previous ones):
#include_dirs=/usr/include/pgsql:/usr/include/pgsql/server
# If postgresql is installed somewhere weird (i.e., not in your runtime library
# path like /usr/lib), just add the right path in "library_dirs" and any extra
# libraries required to link in "libraries".
library_dirs=/usr/local/pgsql/lib
libraries=/usr/lib
At this point we can compile and install the package:
How to install MySQL and Rails on Osx Snow Leopard
Hello everyone.
These days I’ve bought a MacBook Pro with Osx Snow Leopard 64-bit pre installed.
Having to develop mainly in Rails and Django on MySQL and PostgreSQL, I’ve found some difficulties to install and properly configure all packages.
The main problems were due to the presence of 32bit libraries and other at 64bit. For example, Python or Ruby (already installed by default) were compiled at 32bit. Everything seems to work best, however, until you try to install the gem “mysql” or the eggs “psycopg2″ or “MySQL_python”.
These database drivers, having to be “compiled”, generate many problems of incompatibility between different architectures.
In this first tutorial, we’ll see how to install MySQL and Rails 3.0. In the next we’ll see how to proceed with Django and PostgreSQL.
The approaches taken by me are simply the result of googling and other online tutorials. So I’ll link every resource I’ve used.
Regarding the installation of MySQL, I’ve mainly followed the Hivelogic tutorial.
See below the key points that allowed a successful installation.
Step 1: Set the $PATH environment variable
Open a terminal and set the $PATH environment variable in order to link the correct folders in /usr/local/
Add, if it does not exist, this line at the bottom of the .profile file:
and reload the $PATH in this way:
To verify that our $PATH contains the paths set above, type the following command:
Step 2: Download MySQL
Create a new folder to download the sources and compile them:
cd ~/src
Download the latest version available at the time of writing this tutorial:
Step 3: Compile and Install
Build and install MySQL with the following commands:
cd mysql-5.1.47
./configure --prefix=/usr/local/mysql --with-extra-charsets=complex \
--enable-thread-safe-client --enable-local-infile --enable-shared \
--with-plugins=innobase
make
sudo make install
At this point we create a mysql user and a default database.
sudo ./bin/mysql_install_db --user=mysql
sudo chown -R mysql ./var
cd ..
Then we can create scripts to start and stop the MySQL server using the command line.
We create a bin folder in our home and the following file, also giving execute permissions:
touch mysqlscript
chmod +x mysqlscript
Edit the file you’ve just created by inserting the following body:
start()
{
echo -n "Starting MysSQL server"
cd /usr/local/mysql ; sudo /usr/local/mysql/bin/mysqld_safe
return
}
stop()
{
echo -n "Stopping MySQL server"
cd /usr/local/mysql ; sudo /usr/local/mysql/bin/mysqladmin -u root -p shutdown
return
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "Usage: {start|stop|restart}"
exit 1
;;
esac
exit $?
At this point you can start the server by typing “mysqlsript start” in the console. To stop the server just type “mysqlscript stop”.
We are now able to install Ruby on Rails. As mentioned at the beginning of the article, system Ruby is compiled at 32bit.
If you want to compile from source at 64bit and place it in /usr/local/ ruby just follow this tutorial.
Since it is my intention to use instead rvm, I’ll settle for a system level Ruby at 32bit and I prefer to compile every new virtual environment at 64bit (from this tutorial)
Therefore proceed as follows:
Step 1: Install rvm
Launch in a console this command and follow the instructions:
Step 2: Install Ruby 1.9.2
Type the following commands and verify that you have version 1.9.2 installed.
rvm 1.9.2
ruby -v
Step 3: Create a Rails3 gemset
Create a Rails3 gemset and activate it as default:
rvm 1.9.2@rails3 --default
Step 4: Install Rails3 and database drivers for SQLite and MySQL
env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
gem install rails
In this tutorial we’ve then installed MySQL and Ruby 1.9.2 from source. Now that all the packages have been compiled to 64bit we should not find errors while installing other application’s gems.
How to create multiple Django environments using virtualenv
Often we must work on various Django applications, each one dependent on different set of libraries.
For examples, we are working on a Django 1.1 project and and we need to start a new one with the latest version of the framework.
The best solution is to keep the various environments separate, in order to ensure that each project accesses only the libraries which it depends.
Python offers us virtualenv, a tool to create multiple isolated environments. Each environment is completely independent from the others and from the installed packages in system’s site-packages folder.
Let’s then see how to create a virtualenv and install the latest version of Django (today 1.2.1).
1- Install python-setuptools
The first thing to do is to install python-setuptools in order to have access to the command easy_install and to easly download packages. Alternatively, you can get the same results using the package pip.
Let us open a console and type the following commands:
Reading package lists... Done
Building dependency tree
Reading state information... Done
python-setuptools is already the newest version.
0 upgraded, 0 newly installed, 0 to remove and 132 not upgraded.
stefano@stefano-laptop:~$
2- Install virtualenv
The second step is to install the package virtualenv using easy_install:
[sudo] password for stefano:
Searching for virtualenv
Reading http://pypi.python.org/simple/virtualenv/
Reading http://virtualenv.openplans.org
Best match: virtualenv 1.4.9
Downloading http://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.4.9.tar.gz#md5=c49067cab242b5ff8c7b681a5a99533a
Processing virtualenv-1.4.9.tar.gz
Running virtualenv-1.4.9/setup.py -q bdist_egg --dist-dir /tmp/easy_install-3D0IWT/virtualenv-1.4.9/egg-dist-tmp-xe4LRm
warning: no previously-included files matching '*.*' found under directory 'docs/_templates'
Adding virtualenv 1.4.9 to easy-install.pth file
Installing virtualenv script to /usr/local/bin
Installed /usr/local/lib/python2.6/dist-packages/virtualenv-1.4.9-py2.6.egg
Processing dependencies for virtualenv
Finished processing dependencies for virtualenv
3- Create the virtual environment
Once installed the package virtualenv we will need to create a space to host multiple environments. In my case, I’ve chose to create a directory in my home rather than in another writable folder in the filesystem.
stefano@stefano-laptop:~/Progetti/Python$
stefano@stefano-laptop:~/Progetti/Python$ mkdir virtualenvs
stefano@stefano-laptop:~/Progetti/Python$ cd virtualenvs/
stefano@stefano-laptop:~/Progetti/Python/virtualenvs$
Now we create the real virtualenv, called “Django-1.2-env. Note the argument added to the command virtualenv, –no-site-packages: so our virtual environment will have only a minimal set of libraries in his site-packages folder. We also note that all next commands will not need access as super user via sudo.
New python executable in django-1.2-env/bin/python
Installing setuptools............done.
4- Download Django in /tmp
Open a new console and download the tarball with the latest version of the framework Django. For simplicity, I’ve downloaded the package to /tmp.
stefano@stefano-laptop:/tmp$ wget http://www.djangoproject.com/download/1.2.1/tarball/
--2010-08-28 11:20:58-- http://www.djangoproject.com/download/1.2.1/tarball/
Resolving www.djangoproject.com... 64.207.133.18
Connecting to www.djangoproject.com|64.207.133.18|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: http://media.djangoproject.com/releases/1.2/Django-1.2.1.tar.gz [following]
--2010-08-28 11:20:59-- http://media.djangoproject.com/releases/1.2/Django-1.2.1.tar.gz
Resolving media.djangoproject.com... 64.207.133.30
Connecting to media.djangoproject.com|64.207.133.30|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6248006 (6.0M) [application/octet-stream]
Saving to: `Django-1.2.1.tar.gz'
100%[======================================>] 6,248,006 590K/s in 12s
2010-08-28 11:21:11 (523 KB/s) - `Django-1.2.1.tar.gz' saved [6248006/6248006]
5- Installing Django in virtualenv
At this point, unpack Django and activate the virtualenv (via the command “source virtualenvdir/bin/activate”). Once activated virtualenv, we’ll find his name in the shell: (django-1.2-env)stefano@stefano-laptop
stefano@stefano-laptop:/tmp/Django-1.2.1$ source /home/stefano/Progetti/Python/virtualenvs/django-1.2-env/bin/activate
(django-1.2-env)stefano@stefano-laptop:/tmp/Django-1.2.1$
(django-1.2-env)stefano@stefano-laptop:/tmp/Django-1.2.1$ python setup.py install
We verify here that Django is installed properly in the virtual site-packages:
django easy-install.pth setuptools-0.6c11-py2.6.egg
Django-1.2.1-py2.6.egg-info pip-0.7.2-py2.6.egg setuptools.pth
6- Create a new project
At this point, we can create a new project using the command django-admin.py as required by the framework.
(django-1.2-env)stefano@stefano-laptop:~/Progetti/Python$
(django-1.2-env)stefano@stefano-laptop:~/Progetti/Python$ django-admin.py startproject newdjangoapp
(django-1.2-env)stefano@stefano-laptop:~/Progetti/Python$ cd newdjangoapp/
(django-1.2-env)stefano@stefano-laptop:~/Progetti/Python/newdjangoapp$ ls
__init__.py manage.py settings.py urls.py
In conclusion, we can say that virtualenv is an excellent solution to create environments fully independent and designed to accommodate each individual project. In this way, we avoid conflicts of libraries and have the opportunity to try new configurations without creating problems for other applications.
Design Patterns in Ruby: Chain of Responsibility
Today’s post discusses the first of the behavioral pattern shown by the GoF, the chain of responsibility.
This pattern expects a series of commands to be executed and a set of objects capable to handle them.
Each of these “handler” objects can send the command to the next handler in the chain if it is not able to carry it out.
A mechanism also exists for adding new handler objects to the end of this chain.
To show this pattern, we take as example a real situation very familiar to web developers.
Suppose that the manager must deliver a new web project.
To realize the entire project should be conducted several heterogeneous activities such as design the user interface, develop the application, write the user manual, deploy the application.
The manager doesn’t have all required skills but he can rely on a pool of developers.
When the activity reaches a developer, this can solve it or, if he is unable to, send it to a colleague.
In this way a chain of responsibility is formed, where each actor specializes in solving only certain types of requests.
Let’s see how to carry out this scenario in Ruby.
Is therefore necessary that every element in the chain has the ability to “forward” the request to the next if he is not able to manage it.
So we create a module that defines this common logic and removes code duplication.
module Chainable
def next_in_chain(link)
@next = link
end
def method_missing(method, *args, &block)
if @next == nil
puts "This request cannot be handled!"
return
end
@next.__send__(method, *args, &block)
end
end
As you can see, the next_in_chain method provides the next element in the chain.
To meet demands that can not be managed, I’ve used the method_missing “pattern” : when the request can not be managed by the actor (e.g. the invoked method is not defined in the class), it will be forwarded to the next.
Now define the players of our example:
class WebManager
include Chainable
def initialize(link = nil)
next_in_chain(link)
end
def deliver_application
design_interface
build_application
write_documentation
deploy_application
puts "#{self.class.to_s}: Application delivered"
end
end
class WebDeveloper
include Chainable
def initialize(link = nil)
next_in_chain(link)
end
def build_application
puts "#{self.class.to_s}: I'm building the application"
end
def deploy_application
puts "#{self.class.to_s}: I'm deploying the application"
end
end
class WebDesigner
include Chainable
def initialize(link = nil)
next_in_chain(link)
end
def design_interface
puts "#{self.class.to_s}: I'm designing the interface"
end
end
class TechnicalWriter
include Chainable
def initialize(link = nil)
next_in_chain(link)
end
def write_documentation
puts "#{self.class.to_s}: I'm writing the documentation"
end
end
At this point we simulate our chain by running the following code:
require 'models.rb'
provider = WebManager.new(WebDeveloper.new(WebDesigner.new(TechnicalWriter.new)))
provider.deliver_application
provider.make_support
and the output will be:
WebDesigner: I’m designing the interface
WebDeveloper: I’m building the application
TechnicalWriter: I’m writing documentation
WebDeveloper: I’m deploying the application
WebManager: Application delivered
This request cannot be handled!
In conclusion, the use of this pattern is useful when we deal with heterogeneous requests and we want to make sure that these are best handled by a specific handler.
It can also be used when we run commands in sequence, where each element forwards the coming command to the next handler.
An alternative to this pattern could be a decorator, able to add capacity to a specific handler. In this case a single handler will completely manage all requests.
In these first three articles of the series we have analyzed the first pattern shown by the GoF for each category.
In the coming articles I will no more follow the order set by the GoF but I’ll analyze the most useful patterns to address the most common needs.
Source code here
Previous posts from this series:
Design Patterns in Ruby: Introduction
Design Patterns in Ruby: Abstract Factory
Design Patterns in Ruby: Adapter
Design Patterns in Ruby: Adapter
This second post of the series leaves for a moment the creational patterns and speaks about one of the most important structural pattern: the Adapter.
The purpose of an adapter is “to convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.”
Suppose therefore to have two classes, PalGame and NtscGame, that extend a superclass Game. These subclasses respectively expose two methods: play and run.
class Game
attr_accessor :title
def initialize(title)
@title = title
end
end
class PalGame < Game
def play
puts "I am the Pal version of #{@title} and I am running!"
end
end
class NtscGame < Game
def run
puts "I am the NTSC version of #{@title} and I am running!"
end
end
We then subclass a Console class with PalConsole and NtscConsole. These two classes are expected to talk with, respectively, games of kind PalGame and NtscGame.
class Console
end
class PalConsole < Console
def play_game(game)
game.play
end
end
class NtscConsole < Console
def run_game(game)
game.run
end
end
How can see, the method play_game of a PalConsole will call the play method of the game, the NtscConsole instead will invoke the run method.
Our goal is to let a PalConsole to run games of kind NtscGame.
Below we’ll follow the line traced by the GoF and we’ll build an Adapter class that provides the play method needed by the PalConsole’s interface:
class NtscToPalAdatper
attr_accessor :game
def initialize(game)
@game = game
end
def play
@game.run
end
end
As you can see, the adapter exposes a simple play method that calls the run method of the NTSCGame.
The following code:
require 'models.rb'
require 'adapters.rb'
console = PalConsole.new
final_fantasy = NtscGame.new("Final Fantasy")
adapter = NtscToPalAdatper.new(final_fantasy)
console.play_game(adapter)
will produce this output:
I am the NTSC version of Final Fantasy and I am running!
We see at this point some alternatives to further exploit the potential of Ruby.
One possibility is to take advantage of the Ruby’s “open classes”. The language makes possible to add methods to an already loaded class in this way:
require 'models.rb'
console = PalConsole.new
class NtscGame < Game
def play
run
end
# alternatively for this simple example we can define an alias:
# alias play run
end
final_fantasy = NtscGame.new("Final Fantasy")
double_dragon = NtscGame.new("Double Dragon")
console.play_game(final_fantasy)
console.play_game(double_dragon)
As we can see, we’ve added at runtime the method play for a NtscGame game.
The above code produces the following output:
I am the NTSC version of Final Fantasy and I am running!
I am the NTSC version of Double Dragon and I am running!
Note however that with this solution we added the play method to the entire class NtscGame. All instances that are created will be equipped with the play method.
This type of action is very risky because it could not guarantee compatibility with other libraries, for example because of name clash.
Another alternative would be to use the “singleton classes”.
Ruby makes it possible to change a single instance of a class, by creating an anonymous class as its superclass that implements the new defined method.
There are several possibilities to implement these singleton classes. Let’s see in the following snippet some implementations.
require 'models.rb'
console = PalConsole.new
#1 - creating a singleton class
final_fantasy = NtscGame.new("Final Fantasy")
def final_fantasy.play
run
end
console.play_game(final_fantasy)
#2 - adding methods opening the singleton class directly
winning_eleven = NtscGame.new("Winning Eleven")
class << winning_eleven
def play
run
end
end
console.play_game(winning_eleven)
#3 - adding methods from a module
thunderforce = NtscGame.new("Thunderforce")
module Foo
def play
run
end
end
thunderforce.extend(Foo)
console.play_game(thunderforce)
#4 - adding methods inside an instance_eval call
dragons_lair = NtscGame.new("Dragons Lair")
dragons_lair.instance_eval <<EOT
def play
run
end
EOT
console.play_game(dragons_lair)
All implementations will provide the same type of output:
I am the NTSC version of Final Fantasy and I am running!
I am the NTSC version of Winning Eleven and I am running!
I am the NTSC version of Thunderforce and I am running!
I am the NTSC version of Dragons Lair and I am running!
In this post we have therefore seen the usefulness of this design patterns and how Ruby allows the developer to “leave” the classic implementation suggested in order to better take advantage of the potentiality of the language.
source code here
Previous posts from this series:
Design Patterns in Ruby: Introduction
Design Patterns in Ruby: Abstract Factory
Design Patterns in Ruby: Abstract Factory
An abstract Factory provides a common interface for creating families of related objects together.
The client object does not bother to build objects directly, but it calls the methods provided by this common interface.
Below is showed one possible implementation of an abstract Factory and its concrete Factories that implement it.
Suppose we have two categories of games as a model classes:
class Game
attr_accessor :title
def initialize(title)
@title = title
end
end
class Rpg < Game
def description
puts "I am a RPG named #{@title}"
end
end
class Arcade < Game
def description
puts "I am an Arcade named #{@title}"
end
end
How we can see, both models derive from a common superclass Game.
Let’s define the Factories delegate to build these objects:
module MyAbstractGameFactory
def create(title)
raise NotImplementedError, "You should implement this method"
end
end
class RpgFactory
include MyAbstractGameFactory
def create(title)
Rpg.new title
end
end
class ArcadeFactory
include MyAbstractGameFactory
def create(title)
Arcade.new title
end
end
Note that we have defined the abstract factory (MyAbstractGameFactory) as a module: it defines the abstract method that must be implemented by the class that includes it.
RpgFactory and ArcadeFactory represent the two concrete factories responsible to build, respectively, Arcade and RPG games.
The code of a GameStore, that can provide games basing on the needs of the customer, will be defined as follows:
def initialize(number_of_games, game_type)
if game_type == :rpg
title = 'Final Fantasy'
game_factory = RpgFactory.new
elsif game_type== :arcade
title = 'Double Dragon'
game_factory = ArcadeFactory.new
end
@games = []
number_of_games.times do |i|
@games << game_factory.create("#{title} #{i+1}")
end
end
def show_games
@games.each {|game| game.description}
end
end
At this point, launching the following file main.rb
require 'models.rb'
require 'factories.rb'
game_store = GameStore.new(2, :rpg)
game_store.show_games
game_store = GameStore.new(5, :arcade)
game_store.show_games
we’ll get the following output in console:
I am a RPG named Final Fantasy 1
I am a RPG named Final Fantasy 2
I am an Arcade named Double Dragon 1
I am an Arcade named Double Dragon 2
I am an Arcade named Double Dragon 3
I am an Arcade named Double Dragon 4
I am an Arcade named Double Dragon 5
At this point we can optimize our Factories in order to take advantage of the potential offered by Ruby:
class GameFactory
include MyAbstractGameFactory
def initialize(game_class)
@game_class = game_class
end
def create(title)
@game_class.new title
end
end
class GameStore
def initialize(number_of_games, game_type)
c = Object.const_get(game_type.to_s.capitalize)
game_factory = GameFactory.new(c)
if game_type == :rpg
title = 'Final Fantasy'
elsif game_type == :arcade
title = 'Double Dragon'
end
@games = []
number_of_games.times do |i|
@games << game_factory.create("#{title} #{i}")
end
end
def show_games
@games.each {|game| game.description}
end
end
As we can see, now a single concrete factory GameFactory continues to build Arcade and RPG basing on the need of the moment.
This will allow a system to be independent from the implementation of concrete objects and that the client, through the interface, to use the different product families.
Note that in both examples the definition of MyAbstractGameFactory was made only for educational purposes and was used to “simulate” in Ruby an abstract method of an abstract class.
The output generated by invoking this new GameStore is the same as seen in the previous example.
All code available in this repository on GitHub: design_patterns_in_ruby
Previous posts from this serie:
Design Patterns in Ruby: Introduction
Design Patterns in Ruby: Introduction
Hello everyone.
With this post I would like to start a serie of articles concerning the description of the most common design patterns and how these can be applied with the Ruby language.
In the world of software engineering you don’t need to reinvent the wheel to face daily problems: these problems are often very common and in most cases they require a similar resolution.
Christopher Alexander says that “each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such way that you can use this solution a million times over, without ever doing it the same way twice“.
In 1995, Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, starting from the work of Christopher Alexander and Kent Beck, have published “Design Patterns: Elements of Reusable Object-Oriented Software.” With this book, the four authors (from then knowned as GoF, Gang of Four) have proposed solutions to the 23 most common and recurring problems in software engineering.
The GoF have analyzed and divided the 23 patterns into 3 categories:
Creational patterns
* Abstract Factory
* Builder
* Factory Method
* Prototype
* Singleton
Structural patterns
* Adapter
* Bridge
* Composite
* Decorator
* Facade
* Flyweight
* Proxy
Behavioral patterns
* Chain
* Command
* Interpreter
* Iterator
* Mediator
* Memento
* Observer
* State
* Strategy
* Template
* Visitor
We will see during this series all the most common patterns: we’ll analyze the problem and define a solution if possible using the potential provided by the Ruby language.
All showed code will be freely accessible in an out repository on GitHub at this address: design_patterns_in_ruby.