tag:blogger.com,1999:blog-42298683400144837542024-03-14T01:09:00.303-07:00Rails hotspotmgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.comBlogger12125tag:blogger.com,1999:blog-4229868340014483754.post-22992783399875822382012-03-01T14:17:00.004-08:002012-03-01T14:26:33.079-08:00Gemfile.localHey guys,<br /><br />From a while now, I've been looking for a way to add local dependencies to gems within my Rails project, but bundler is not defined to allow that by default :P<br /><br />For me, as I work on a distributed team, with different operating systems and all, there are times that gems need to be different, one really silly sample is growl (a notification tool that is only available on Mac, but I use Linux so I always have to remove that dependency manually...)<br /><br />Finally, I reach this post:<br /><br />http://madebynathan.com/2010/10/19/how-to-use-bundler-with-plugins-extensions/<br /><br />Which give me a really simple idea to accomplish what I wanted. Basically you can add this to the end of your Gemfile:<br /><br /><code><br />Dir.glob(File.join(File.dirname(__FILE__), "Gemfile.local")) do |gemfile|<br /> eval(IO.read(gemfile), binding)<br />end<br /></code><br /><br />And then having Gemfile.local ignored on .gitignore, from there is trivial.<br /><br />You just need to add any local dependency you need on Gemfile.local.<br /><br />Hope it is useful for anybody out there ;)mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com0tag:blogger.com,1999:blog-4229868340014483754.post-79887732614719924202010-06-30T08:15:00.000-07:002010-07-03T10:06:20.906-07:00Ruby Time TrackerHi guys,<br /><br />If you are one of those people that works as freelancer or want to keep track of your time for some motive, then this post may interest you.<br /><br />There was a point in time that I used web sites (free or paid) to track my working time, but I got annoyed because of the time loose reaching those pages or the network issues you might have while using them... and as you may agree with me, nothing is much efficient that using command line tools. So, I've just write a new gem called RTT to track time from the command line. Here is how I use it all the time:<br /><br />$ rtt 'new task description' (when I started the timer)<br /><br />To stop (or pause) it, just type:<br /><br />$ rtt stop (pause)<br /><br />Then if you've a task already paused, then you can:<br /><br />$ rtt resume<br /><br />Or, to restart an existing task already finished, you can type:<br /><br />$ rtt 'task description'<br /><br />Then the new time, will be sum to the existing accumulated time for that task.<br /><br />Also, I always want to check how I'm doing today, so I've type this:<br /><br />$ rtt list<br /><br />And I get and output like this:<br /><br />Task List<br />=========<br />Task: <task-description-1> || Client: <client-1> || Project: <project-1> || User: <user-1> || Elapsed time: 0h58m<br />Task: <task-description-2> || Client: <client-1> || Project: <project-1> || User: <user-1> || Elapsed time: 2h20mm<br />Task: <task-description-3> || Client: <client-1> || Project: <project-1> || User: <user-1> || Elapsed time: 0h25m<br />...<br /><br />Having a time tracking tool for the command line was the main propose to gain time, and avoid all network issues for tracking time using web sites. But also, I've included in this gem a way to generate PDF's documents (or invoices if you will), so that it is easier for you to send your worked hours, using this command:<br /><br />$ rtt report<br /><br /><filename>So, If it sounds like it feet for you, you can install it as usual, with:<br /><br />$ [sudo] gem install rtt<br /><br />There are several more options for filtering tasks for the list, delete or report command, you can check for those here: <a href="http://github.com/marklazz/rtt">http://github.com/marklazz/rtt</a><br /><br />Enjoy!</filename><filename></filename>mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com1tag:blogger.com,1999:blog-4229868340014483754.post-79871148082418275642010-03-18T17:49:00.000-07:002010-03-19T08:38:53.830-07:00Substracting sets with SQLHey guys,<br /><br />Long time no see!<br /><br />I've wanted to mentioned about an sql's trick that I used on my current project and was pretty handy!<br /><br /><span style="font-size:180%;">The Problem</span><br /><br />Let's say you have a table of users and wanted to write a query to retrieve the rows that meet certain criteria and doesn't meet other... Also, we don't want to use NOT EXISTS statement (as it is too expensive in terms of performance). In short, you want something like this:<br /><br />You_want = Some_Set_Users (I) - Another_Users_Set (II)<br /><br />This can be accomplished by simple execute a couple of queries, one for (I) and another for (II) and just subtract them using the programming language. For instance, in Rails you just can do the following:<br /><br />query_1 = User.find(..conditions for set (I)..)<br />query_2 = User.find(..conditions for set (II)..)<br />..and then just using ruby..<br />result = query_1 - query_2<br /><br />But, this has a couple of drawbacks:<br /><br />1) This would load all records into memory which it not good, as it takes time and occupies valuable space<br />2) And two more importantly in most cases, it's throughput is worse than doing it directly on the database.<br /><br />So, which is the alternative ?<br /><br /><span style="font-size:180%;">The Solution<br /></span><br />Although, joining tables is not the cheapest operation available for a database, still it does the job pretty efficiently and it can be tunned to be pretty fast depending on your needs.<br /><br />So, in this case, I propose to use the following strategy:<br /><br /><span style="font-size:130%;"><span style="font-style: italic;">select some_users_set.* from users as some_users_set LEFT JOIN</span><br /><span style="font-style: italic;"> (select inner_another_users_set from inner_users as another_users_set where ..conditions for another_users_set...) as another_users_set on some_users_set.id = another_users_set.id</span><br /><span style="font-style: italic;">where ..conditions for some_users_set.... AND another_users_set.id IS NULL</span><br /></span><br /><br /><span style="font-size:180%;">The Explanation<br /></span><br />The idea behind this sql is to do a LEFT JOIN of the two sets (I and II) that we want to make the subtraction.<br /><br />This way, as a LEFT JOIN's operation will always match for every row on both sets, when there is no row on set II for a given row on I, we will get all fields for set II filled with NULL (as there is no correspondent on the join). And those cases, are exactly the ones we are looking for, the ones that exists on set I but there is no correspondent on set II.<br /><br />I find this trick very useful, I hope you could benefit from it too!<br /><br />Let me know your thoughts, and obviously any questions if there is something unclear!<br /><br />See ya!mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com0tag:blogger.com,1999:blog-4229868340014483754.post-85232985579320994402009-06-30T08:40:00.000-07:002009-06-30T09:55:20.610-07:00campfire-pattern-alertPeople,<br /><br />I wanted to share with you a simple script published as open source: (git://github.com/mgiorgi/campfire-pattern-alert.git)<br /><br />I developed this software because I wanted some kind of alerts, when people mentioned my name in a Campfire's room. In fact, our team used to work that way, when someone wanted to talk to me, they just typed: 'Marcelo'. And I supposed to check that messages below that.<br /><br />For that, I though, it would be great if there were a Desktop tool that can connect to campfire and just alert me when that happened. But, unfortunately, for Linux users(*), like me, there is no good solution to filter the irrelevant messages and alert me with the ones I want. <br /><br />So, I decided to came up with this tiny tool that does exactly that job!<br /><br />An example of the usage of it (after configuring the proper jabber/campfire connection information in jabber-configuration.rb and campfire-confirguration.rb respectivly):<br /><br />$ ./campfire-listener.sh Marcelo JABBER<br /><br />Then after a couple of logged messages, every message that contained 'Marcelo' would be sent to the jabber account that I want. In fact, I decided to forward the following 5 messages (this amount can be changed within a constant in campfire-listener.sh) after an occurrence of 'Marcelo' (in this case).<br /><br />General usage would look like: ./campfire-listener.sh pattern output. <br /><br />Where 'pattern' could be a regular expression and 'output' stands for STDOUT (if output parameter is not set, this is considered the default value) or JABBER.<br /><br />Well, hope it helps you, as well it helped me! Let me know what you think ;)<br /><br />(*): I knew that such a solution exists for Mac OS, for instance.mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com0tag:blogger.com,1999:blog-4229868340014483754.post-3163124651991945312009-06-08T07:59:00.000-07:002009-06-08T09:30:56.527-07:00A first look at God and MonitHey people!,<br /><br />Lately I've been configuring a production server which basically is composed of a mongrel cluster and some other daemons that we use (backgroundrb, i.e.). To keep alive both mongrel instances and our daemons we needed some monitor tool to help us know when something went wrong and restart those processes if needed.<br /><br />For that matter, I investigate the following tools: <span style="font-weight: bold;">Monit</span> and <span style="font-weight: bold;">God</span>.<br /><br /><span style="font-size:180%;">Monit</span><br /><br />Monit (http://mmonit.com/monit/) is a great utility for managing and monitoring, processes, files, directories and devices on a Unix system.<br /><br /><span style="font-size:130%;">Installation</span><br /><br />It's very straight-forward:<br /><ol><li>Download the latest version on http://mmonit.com/monit/download/</li><li>tar zxvf monit-x.y.z.tar.gz</li><li>cd monit-x.y.z</li><li>./configure</li><li>make && sudo make install (sudo if necessary)<br /></li></ol><span style="font-size:130%;">Usage</span><br /><br />After installing it, we need to create a configuration file. Monit would look for this file in the following locations:<br /><ul><li>~/.monitrc</li><li>/etc/monitrc</li><li>/.monitrc</li></ul>You can choose where you want to locate that configuration file. As an example, below I show the configuration file I used for my mongrel cluster:<br /><br /><span style="font-size:85%;"><span style="font-style: italic;">#.monitrc</span><span style="font-style: italic;"><br /></span><span style="font-style: italic;">set daemon 30</span><br /><span style="font-style: italic;">set logfile /home/myuser/monit/monit.log</span><br /><span style="font-style: italic;">set httpd port 9111</span><span style="font-style: italic;"><br /> allow remotehost<br /></span><span style="font-style: italic;"> allow admin:admin # Allow Basic Auth</span><br /><span style="font-style: italic; font-weight: bold;">check process mongrel_cluster_3010</span> <span style="font-style: italic;"> with pidfile "/var/www/myapp/current/tmp/pids/mongrel.3010.pid"</span><br /><span style="font-style: italic;"> start program = "/usr/local/bin/ruby /usr/local/bin/mongrel_rails start -d -e production -a 127.0.0.1 -c /var/www/myapp/current --user myuser --group deploy -p 3010 -P tmp/pids/mongrel.3010.pid -l log/mongrel.3010.log"</span><br /><span style="font-style: italic;"> stop program = "/usr/local/bin/ruby /usr/local/bin/mongrel_rails stop -p 3010 -P /var/www/myapp/current/tmp/pids/mongrel.3010.pid"</span><br /><span style="font-style: italic;"> <span style="font-weight: bold;">if failed port 3010 protocol http</span> # check for response</span><br /> <span style="font-style: italic;"><span style="font-weight: bold;">with timeout 10 seconds</span></span> <span style="font-style: italic;"> <span style="font-weight: bold;"><br /> then restart</span></span><br /><span style="font-style: italic;"> group mongrel</span><br /><span style="font-style: italic;"><span style="font-weight: bold;">check process mongrel_cluster_3011</span> with pidfile "/var/www/myapp/current/tmp/pids/mongrel.3011.pid"<br />start program = "/usr/local/bin/ruby /usr/local/bin/mongrel_rails start -d -e production -a 127.0.0.1 -c /var/www/myapp/current --user lokkedc --group deploy -p 3011 -P tmp/pids/mongrel.3011.pid -l log/mongrel.3011.log"<br />stop program = "/usr/local/bin/ruby /usr/local/bin/mongrel_rails stop -p 3011 -P /var/www/myapp/current/tmp/pids/mongrel.3011.pid"<br /><span style="font-weight: bold;">if failed port 3011 protocol http</span> # check for response<br /> <span style="font-weight: bold;">with timeout 10 seconds</span><br /> <span style="font-weight: bold;">then restart</span><br />group mongrel</span><span style="font-style: italic;"></span><br /><br /></span>You can check the complete documentation of the commands allowed for monit in http://mmonit.com/monit/documentation/monit.html.<br /><br />But I just wanted to show you a simple example that checks for the availability of the mongrel instances (in this case I just configured a couple of those). As you may notice, I have to configure each instance separatly (besides they both belongs to the same group, so I can start/stop all of those at the same time).<br /><br />Other thing to notice, is the events that I want to monitor. In this case I've configured to restart a given mongrel's instance (the fragment that takes care of this is highlighted in the code below) when there is no response from it. But there are many other events, such us memory usage, cpu time, that can be measured to alert the administrator (vía email).<br /><br /><span style="font-size:130%;">Run it!</span><br /><br />To start it we just execute: <span style="font-style: italic;">moint</span>. And we can see how the logs starts to populate:<br /><br /><span style="font-style: italic;font-size:85%;" >[UYT Jun 5 19:12:15] info : monit: generated unique Monit id 15fdb0bdb830b0a114a3831d995ec32e and stored to '/home/myuser/.monit.id'<br />[UYT Jun 5 19:12:15] info : Starting monit daemon with http interface at [*:9111]<br />[UYT Jun 5 19:12:15] info : Starting monit HTTP server at [*:9111]<br />[UYT Jun 5 19:12:15] info : monit HTTP server started<br />[UYT Jun 5 19:12:15] info : 'willy' Monit started<br />[UYT Jun 5 19:12:34] info : Shutting down monit HTTP server<br />[UYT Jun 5 19:12:35] info : monit HTTP server stopped<br />[UYT Jun 5 19:12:35] info : monit daemon with pid [21560] killed<br />[UYT Jun 5 19:12:35] info : 'willy' Monit stopped<br />[UYT Jun 5 19:13:42] info : Starting monit daemon with http interface at [*:9111]<br />[UYT Jun 5 19:13:42] info : Starting monit HTTP server at [*:9111]<br /></span><br />One of the greatest things about monit is that it offers a web interface (as you may notice from the configuration file). Which looks pretty nice!<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3cOk0ogCvTRW_srS68L2xm5l37Q9uGOsJeXcNXQ5UX8BG3s6zlkMrk5Sre_uzXkoCRpCyn-TQ_IMborRprLtprlNChplI4B5_txK73eJabDqVpaNmWtRoGbYXv5xGrKW3PAgL-1Sz1KpB/s1600-h/monit-index-small.png"><img style="cursor: pointer; width: 250px; height: 156px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3cOk0ogCvTRW_srS68L2xm5l37Q9uGOsJeXcNXQ5UX8BG3s6zlkMrk5Sre_uzXkoCRpCyn-TQ_IMborRprLtprlNChplI4B5_txK73eJabDqVpaNmWtRoGbYXv5xGrKW3PAgL-1Sz1KpB/s320/monit-index-small.png" alt="" id="BLOGGER_PHOTO_ID_5344987794427020738" border="0" /></a><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtHuJ6GUaNhLuFdyYSVTla_0bZuZsz0VfYWmPUAx0pFuhl9XLkomigQ-QBzMA2DjLVvpRsg781LygkL_kQgQJ6Eq56MTfQ0Q2fuU2jYcGlI4qwK0COxjdlauB2T3a9kcQGLZJoXjMIZgOU/s1600-h/monit-1-starting-small.png"><img style="cursor: pointer; width: 250px; height: 156px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtHuJ6GUaNhLuFdyYSVTla_0bZuZsz0VfYWmPUAx0pFuhl9XLkomigQ-QBzMA2DjLVvpRsg781LygkL_kQgQJ6Eq56MTfQ0Q2fuU2jYcGlI4qwK0COxjdlauB2T3a9kcQGLZJoXjMIZgOU/s320/monit-1-starting-small.png" alt="" id="BLOGGER_PHOTO_ID_5344988343497714978" border="0" /></a><br /><br/><br /><span style="font-size:180%;">God</span><br /><br />God its a newer tool written in ruby and available as a rubygem. For this matter, it is easier to install it also (i.e. [sudo] gem install god).<br /><br /><span style="font-size:130%;">Usage</span><br /><br />We need to create a configuration file, which is entirely written in ruby. This is one the best things about God, because it helps us reduce duplication among configurations, as you can see in the following example (which is intended to monitor the same mongrel cluster mentioned above):<br /><br /><span style="font-size:85%;"> <span style="font-style: italic;">#myapp.god</span><br /><span style="font-style: italic;">RAILS_ROOT = "/var/www/myapp/current"</span><br /><span style="font-style: italic;">%w{3010 3011}.each do |port|</span> <span style="font-style: italic;"> God.watch do |w|</span><br /><span style="font-style: italic;"> </span> <span style="font-style: italic;"> w.name = "mongrel_cluster_#{port}"</span><br /><span style="font-style: italic;"> w.group = 'mongrels'<br /></span><span style="font-style: italic;">w.interval = 30.seconds</span><span style="font-style: italic;"><br />w.start = "mongrel_rails start -c #{RAILS_ROOT} -p #{port} \</span> <span style="font-style: italic;"> -P #{RAILS_ROOT}/tmp/pids/mongrel.#{port}.pid -d -e production"</span><br /><span style="font-style: italic;"> w.stop = "mongrel_rails stop -P #{RAILS_ROOT}/tmp/pids/mongrel.#{port}.pid -e production"<br /></span><span style="font-style: italic;">w.restart = "mongrel_rails restart -P #{RAILS_ROOT}/tmp/pids/mongrel.#{port}.pid -e production"</span> <span style="font-style: italic;"> w.start_grace = 10.seconds</span><span style="font-style: italic;"><br />w.restart_grace = 10.seconds</span><span style="font-style: italic;"><br />w.pid_file = File.join(RAILS_ROOT, "tmp/pids/mongrel.#{port}.pid")</span><span style="font-style: italic;"><br />w.behavior(:clean_pid_file)</span><br /><span style="font-style: italic;"> w.start_if do |start|</span> <span style="font-style: italic;"> start.condition(:process_running) do |c|</span> <span style="font-style: italic;"> <br /> c.interval = 5.seconds</span><br /> <span style="font-style: italic;">c.running = false</span><br /><span style="font-style: italic;"> end</span> <span style="font-style: italic;"> <br />end</span><br /><span style="font-style: italic;">end</span></span><br /><br />As you can see, I can refactor the mongrel's configuration into just one block which is instantiated for each mongrel's port. Besides that, I've also reduce the amount of configuration using RAILS_ROOT constant in many settings (reducing error prune also).<br /><br /><span style="font-size:130%;">Run It!</span><br /><br />To start our God monitoring tool, we just exectue: <span style="font-weight: bold; font-style: italic;">god -c myapp.god</span><br /><br />After which, we can see the log using the following command: <span style="font-style: italic; font-weight: bold;">god log mongrels</span> (to show the logs relative to our group of mongrel instances). Which should display something like this:<br /><br /><span style="font-style: italic;font-size:85%;" >I [2009-06-08 12:59:16] INFO: mongrel_cluster_3010 move 'unmonitored' to 'up'<br />I [2009-06-08 12:59:16] INFO: mongrel_cluster_3010 moved 'unmonitored' to 'up'<br />I [2009-06-08 12:59:16] INFO: mongrel_cluster_3010 [trigger] process is not running (ProcessRunning)<br />I [2009-06-08 12:59:16] INFO: mongrel_cluster_3010 move 'up' to 'start'<br />I [2009-06-08 12:59:16] INFO: mongrel_cluster_3010 before_start: deleted pid file (CleanPidFile)<br />I [2009-06-08 12:59:16] INFO: mongrel_cluster_3010 start: mongrel_rails start -c /var/www/myapp/current -p 3010 -P /var/www/myapp/current/tmp/pids/mongrel.3010.pid -d -e production<br />I [2009-06-08 12:59:27] INFO: mongrel_cluster_3010 moved 'up' to 'up'<br />I [2009-06-08 12:59:27] INFO: mongrel_cluster_3010 [ok] process is running (ProcessRunning)<br /></span><br /><span style="font-size:180%;">Conclusion - My Choice</span><br /><br />Any of those tools gets the job done pretty well. But I decided to use monit, because it is easier/faster for me to manage the process using a web-interface instead of logging into the server via ssh.<br /><br />Besides that, God seems to be a great tool as it provides a way to reduce duplication (because its configuration is defined with ruby code).mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com1tag:blogger.com,1999:blog-4229868340014483754.post-67629005549362167722009-03-26T19:51:00.000-07:002009-03-27T10:53:04.828-07:00Installing Rails Full-stack testing environment: Cucumber + SeleniumHey people!,<br /><br />I've just had to configure an environment for running both plain Cucumber features and enhanced features (the ones that relies on Javascript) with the help of Selenium, so I decide to centralized the information I found in order to install the full-testing stack.<br /><br /><span style="font-weight: bold;font-size:130%;" >Environment</span><br /><br />First of all, I'll be working on Rails 2.3 environment. And I'll assume that you got Rails/Rspec already installed since I had covered that in a previous entry. Also it would help to check http://wiki.github.com/dchelimsky/rspec/configgem-for-rails to fix issues related to Rails > = 2.2 and Rspec.<br /><br />Install cucumber is really easy, you just got to follow the instructions of the cucumber homepage: http://wiki.github.com/aslakhellesoy/cucumber/ruby-on-rails<br /><br /><span style="font-weight: bold;font-size:130%;" >Selenium</span><br /><br />Selenium is a suite of tools, developed by Thoughtworks, to automate web applications testing across many platforms (http://seleniumhq.org/). In particular, it provides us the capability to run features written with Cucumber's semantics within a real web browser. That gives us the benefit to run features that we couldn't test without a browser (i.e. Javascript dependent functionality).<br /><br /><span style="font-weight: bold;font-size:130%;" >Install Selenium within Rails</span><br /><br />1) First of all we need to follow this document: http://wiki.github.com/aslakhellesoy/cucumber/setting-up-selenium (I). As you can see from there you'll need Java > = 1.5 installed in order for selenium server to work.<br /><br />2) Then you should install selenium-client gem for our Rails application, as usual:<br /><br />sudo gem install selenium-client<br /><br />3) After that, we need to install selenium-on-rails plugin. I used the following repository for that matter: http://github.com/paytonrules/selenium-on-rails/tree/master (because in this one some issues with Rails 2.x are solved)<br /><br /><span style="font-weight: bold;font-size:130%;" >Setting up a test environment in our Rails application</span><br /><br />As you can see from (I), it would be nice to organize selenium features and non-selenium features in different directories, as they need different libraries and initialization. So, I decide to organize it like this:<br /><br />features/<br />|-- common<br />| -- step_definitions<br />| `-- common_steps.rb<br />|-- selenium<br />| -- step_definitions<br />| `-- browser_dependent_steps.rb<br />| -- support<br />| `-- env.rb<br />| `-- some_ajax.feature<br />|-- plain<br />| -- step_definitions<br />| `-- webrat_steps.rb<br />| `-- non_ajaxy.feature<br />`-- support<br /> `-- env.rb<br /><br />This way, I maintain the support/env.rb file for general environment configuration. But I used selenium/support/env.rb for the selenium's features. The same concept applies to step_definitions that are separated in: <span style="font-style: italic;">common</span> (used by plain & selenium features), <span style="font-style: italic;">plain</span> (non-selenium features) and the ones for <span style="font-style: italic;">selenium</span>.<br /><br />This separation helps a lot for creating appropiate tasks to run cucumber. I used the following guide to create those tasks http://gist.github.com/73771.<br /><br /><span style="font-weight: bold;font-size:130%;" >Transactional fixtures</span><br /><br />When running test, by default, Rails doesn't save our record into the database (use_transactional_fixtures = false) to isolate test which is a good thing (for our specs for instance). But, for selenium we want to save them! Because selenium server and web server runs in different process (so they don't have visibility for our saved records!). See http://wiki.github.com/aslakhellesoy/cucumber/troubleshooting<br /><br />As it is suggested there, I used database_cleaner (http://github.com/bmabey/database_cleaner/tree/master) to manage transactions for selenium feature's setup.<br /><br /><span style="font-weight: bold;font-size:130%;" >Issue with sqlite3</span><br /><br />Besides that, I've found some complications to run selenium features with sqlite3. In particular, the problem is that I couldn't disable transactional fixtures for that. But using Mysql worked perfectly for me.<br /><br /><span style="font-weight: bold;font-size:130%;" >Selenium API</span><br /><br />For information on Selenium and the methods provided by SeleniumDriver class that represent our browser you can check the following links:<br /><br />http://seleniumhq.org/documentation/core/reference.html<br /><br />http://selenium.rubyforge.org/rdoc/classes/Selenium/SeleniumDriver.html<br /><br />Other interesting tool is Selenium IDE which is a Firefox plugin that can record your actions and write the correspondence script using Selenium API (that can be re-used within your tests)<br /><br />See ya!mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com0tag:blogger.com,1999:blog-4229868340014483754.post-7830913049924208842008-11-30T11:39:00.001-08:002008-12-17T03:41:50.063-08:00Cucumber vs StoryRunnerHi guys!,<br /><br />I'm exploring the capabilities provided by this beautiful framework, as It is going to be used by our team at work. So I decided, to take advantage of that, and study it a little deeper and share with you which my impressions are.<br /><br /><span style="font-size:180%;">Installation</span><br /><br />This is just typing:<br /><br /><span style="font-style:italic;">gem install cucumber</span><br /><br />And to install in your Rails project, execute this line on your vendor/plugins directory:<br /><br /><span style="font-style:italic;">git clone git://github.com/aslakhellesoy/cucumber.git</span><br /><br /><span style="font-size:180%;">Features</span><br /><br />So the first important difference to notice is that Cucumber is about features (not .stories). The idea behind this is to write the old stories in a more BDD way. So, for each new feature that you have to develop, you should first write a file on the <span style="font-style: italic;">features</span> folder (and with .feature extension), and specify the new functionality on it, in such a way it would fail. When the feature is fully implemented, the test should be successful.<br /><br />You can execute a cucumber features like this:<br /><br />* <span style="font-style: italic;">cucumber features/my_new_feature.feature</span> or by executing:<br /><br />* <span style="font-style: italic;">rake features</span> if you configure the Rakefile of you application with the following code:<br /><br /><span style="font-style:italic;">require 'cucumber/rake/task'<br />Cucumber::Rake::Task.new<br /></span><br /><br /><span style="font-size:180%;">Contents of features files</span><br /><br />Cucumber is about Features (instead of StoryRunner's Stories). Inside a Feature, in a similar fashion as StoryRunner, we got to define an Scenario. So one of the first lines of that file should be an Scenario with a key that identifies it. Then every Scenario is composed by a list of one of those keywords: <span style="font-style:italic;">Given</span>, <span style="font-style:italic;">Then</span>, <span style="font-style:italic;">When</span> (same as StoryRunner) AND <span style="font-style:italic;">But</span>. <br /><br />Here is a simple example of a Cucumber's feature:<br /><br /><span style="font-style: italic;">Feature: Serve coffee<br />In order to earn money<br />Customers should be able to<br />buy coffee at all times<br /><br />Scenario: Buy last coffee<br /> Given there are 1 coffees left in the machine<br /> And I have deposited 1$<br /> When I press the coffee button<br /> Then I should be served a coffee<br /></span><br /><br />The definition of what to do within each step (a <span style="font-style:italic;">Given</span>, <span style="font-style:italic;">Then</span>, <span style="font-style:italic;">When</span> or <span style="font-style:italic;">But</span> sentence) is loaded from steps's files in a similar way StoryRunner does. But, Cucumber have an important difference here: they are written using Regular Expressions instead of strings. So you can do something like this:<br /><br /><span style="font-style:italic;">When /(\w+) enters (his|her) email with "(\S+\@\S+)"/ do |name, email|<br /> fills_in field_name, :with => User.find_by_name(name).email<br />end<br /></span><br /><br />As you can see, using Regular Expressions give us more expressiveness while writing our features.<br /><br />Until this point, every seems pretty much the same as StoryRunner, but I found a big improvement here:<br /><br />1) Steps are automatically available from step_definitions (or any sub-folder of features) folder. This is a big difference for reutilization compared with StoryRunner which requires to import explicitly any step's files needed by the story.<br /><br />2) There's a relatively new feature which let's you invoke steps from within other steps. For example, we can have some common steps like: <span style="font-style:italic;">/Given a user with login '(\S+)'/</span>, <span style="font-style:italic;">/And is not logged in/</span>, among others that can be reused in many features (we probably need our user logged in to use most of our site functionality). Then each we can invoke this steps within other steps, like this:<br /><br /><span style="font-style:italic;">#features/steps/login_steps.rb<br />Given /a user with login '(\S+)'/ do<br />..<br />end<br />Given /a password '(\S+)'/ do<br /> ..<br />end<br />Given /is not logged in/ do<br /> ..<br />end<br /><br />#features/steps/somefile_steps.rb<br />Given /User (\S+) with password (\S+) is logged in/ do |username, pass|<br /> Given "a user with login #{username}"<br /> Given "a password #{pass}"<br /> Given is not logged in<br />end<br /><br />#features<br />Feature: Some feature<br /><br /> Scenario: Some scenario description<br /><br /> Given User pepe with password pepe is logged in<br /> And ..<br /> Then ..<br /></span><br />This way, we can reuse most of our steps, making our test more DRY.<br /><br />3) Finally, it is possible to define our stories in a different language than English. But as I won't be using this for now, I didn't realize how interesting this might be..as most of my work is written in English, but you never know :P.<br /><br />That's enough for now, talk to you later ;)mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com0tag:blogger.com,1999:blog-4229868340014483754.post-17988958110888871882008-08-05T08:37:00.000-07:002008-11-21T06:17:16.046-08:00RSpec in Ubuntu: Setting up testing environmentRecently, I had a difficult time when my computer crashed.. After reinstalling Ubuntu desktop, and all the tools I realized how important is to maintain documents about how things are installed and configured :P<br /><br />One of the things that I've been forced to research was rspec (installation and configuration) in order to restore what I've already had. This is the motivation for this entry: document the way I consider useful to install and customize rspec with notifications, and reports, etc.<br /><br /><span style="font-size:180%;">Install the gems</span><br /><br />Assuming that you already have installed rails (<a href="http://wiki.rubyonrails.org/rails/pages/RailsOnUbuntu">http://wiki.rubyonrails.org/rails/pages/RailsOnUbuntu</a>) and an application (let's called it myapp) suitable to use with rspec. In this context, we are going to install the ZenTest gem (which includes rspec & autotest):<br /><code class="typocode_default"><br />gem install ZenTest<br />gem install diff-lcs<br /><br />After that, You'll need to install both rspec and rspec-rails plugins within your Rails application. For that matter, you should check: github.com/dchelimsky/rspec-rails/wikis/home (where it shows the different procedures you should follow to install it depending on your version of Rails).</code><br /><br />After this step, you'll be able to run the autotest tool from the command line.<br /><span style="font-size:180%;"><br />Configure autotest</span><br /><br />Then in the <span style="font-style: italic;">myapp</span> you have to create the configuration file <span style="font-style: italic;">.autotest</span>:<br /><br /><code><br />#!/usr/bin/env ruby<br />require 'autotest/redgreen'<br />def self.notify title, msg, img, pri='low', time=3000<br />`notify-send -i #{img} -u #{pri} -t #{time} '#{msg}'`<br />end<br />Autotest.add_hook :ran_command do |at|<br />results = [at.results].flatten.join("\n")<br />output = results.slice(/(\d+)\s+examples?,\s*(\d+)\s+failures?(,\s*(\d+)\s+not implemented)?(,\s*(\d+)\s+pending)?/)<br />folder = "~/Pictures/autotest/"<br />if output =~ /([123456789]|[\d]{2,})\sfailures?/<br />notify "FAIL:", "#{output}", folder+"rails_fail.png", 'critical', 10000<br />elsif output =~ /[1-9]\d*\spending?/<br />notify "PENDING:", "#{output}", folder+"rails_pending.png", 'normal', 10000<br />else<br />notify "PASS:", "#{output}", folder+"rails_ok.png"<br />end<br />end<br /></code><br />This code hooks in the autotest evaluation process and invokes the command line utility <span style="font-style: italic;">notify-send</span> with the result of the evaluation. It also uses this pictures within their notifications (that enhance the user experience):<br /><table><br /><tbody><tr><br /><td><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc7PU6Awxwa4sZkAdg0WTzVV0rkMw49rgiUeupWRIPfD0ocOgyU1Q-7vMndvR-g5RURJXkq9US0t6vuCojP2ztyQhnpj_W2e4_Dy9poNz_bfCC-OZgR5Hj2LFf6gjKein99YUL_3OCKpg1/s1600-h/rails_fail.png"><img style="display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc7PU6Awxwa4sZkAdg0WTzVV0rkMw49rgiUeupWRIPfD0ocOgyU1Q-7vMndvR-g5RURJXkq9US0t6vuCojP2ztyQhnpj_W2e4_Dy9poNz_bfCC-OZgR5Hj2LFf6gjKein99YUL_3OCKpg1/s400/rails_fail.png" alt="" id="BLOGGER_PHOTO_ID_5231104765283987826" border="0" /></a><br /></td><br /><td><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI6ollk94pEPkazsIQpHrvdt0hDOOq4R9U0rBLPfz5HCX3edtLILBreZNTrR3yeAfWhvkHOvOBJZ-UchMOzSzYp37MBKfPi_pV2Q69y4tSs0TNq9AvGD6SEd_dA_vW-ziMRUvqp61W2Xte/s1600-h/rails_ok.png"><img style="display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI6ollk94pEPkazsIQpHrvdt0hDOOq4R9U0rBLPfz5HCX3edtLILBreZNTrR3yeAfWhvkHOvOBJZ-UchMOzSzYp37MBKfPi_pV2Q69y4tSs0TNq9AvGD6SEd_dA_vW-ziMRUvqp61W2Xte/s400/rails_ok.png" alt="" id="BLOGGER_PHOTO_ID_5231104764520432194" border="0" /></a><br /></td><br /><td><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMPbIOWDnlb-aJko7z1NYFI076sIbWLF-jVM-jr-81E44rAcNHBsS2Q40o8vV2yx4I9WAieweagD5iLPKIm3hV4x4n9fCZI5l9UlO_hf2dl_b-Oai9c3kVrnVC4bZxI8xTrRG7XPTMqD21/s1600-h/rails_pending.png"><img style="display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMPbIOWDnlb-aJko7z1NYFI076sIbWLF-jVM-jr-81E44rAcNHBsS2Q40o8vV2yx4I9WAieweagD5iLPKIm3hV4x4n9fCZI5l9UlO_hf2dl_b-Oai9c3kVrnVC4bZxI8xTrRG7XPTMqD21/s400/rails_pending.png" alt="" id="BLOGGER_PHOTO_ID_5231104765007675714" border="0" /></a><br /></td><br /></tr><br /></tbody></table><br /><br /><span style="font-size:180%;">Configure Ubuntu notifiactions</span><br /><br />Finally, you have to install the following package in your system:<br /><br /><code><br />sudo apt-get install libnotify-bin<br /></code><br /><br />This tool enable us to display pop-ups within Ubuntu's environment.<br /><br /><span style="font-size:180%;">RSpec reports</span><br /><br />Other important file for autotest is /spec.opts, which is located at myapp/spec:<br /><code><br />#myapp/spec/spec.opts<br />--colour<br />--format html:doc/rspec_results.html<br />--format progress<br />--loadby mtime<br />--reverse<br /></code><br />In this configuration I specify that I want autotest to keep running in the console and, at the same time, I want to generate the html report (rspec_results.html).<br /><br /><span style="font-size:180%;"></span><span style="font-size:180%;">Run autotest & see results.</span><br /><br />After you configure all that I mentioned, you are able to execute the utility <span style="font-style: italic;">autotest</span> from myapp directory and obtain the following results:<br /><code><br />marcelo@host:~/workspace/myapp$ autotest<br />loading autotest/rails_rspec<br />/usr/bin/ruby1.8 -S script/spec -O spec/spec.opts spec/controllers/users_routing_spec.rb spec/views/users/show.html.erb_spec<br />...<br /></code><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0oxdnIG8MHUEIP5Wm5DNOyyPHqvK24UH4bLk63qLytSW-g0rN4gMNz_-VPxLjViz37x3xmLKii8ki_IJXtjwvHiEBTXT5I-TIbBBAQOgzxYVVGbsRZZZ0RZTNYqa1GCl-y6f6n0Rm-lu6/s1600-h/notify.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0oxdnIG8MHUEIP5Wm5DNOyyPHqvK24UH4bLk63qLytSW-g0rN4gMNz_-VPxLjViz37x3xmLKii8ki_IJXtjwvHiEBTXT5I-TIbBBAQOgzxYVVGbsRZZZ0RZTNYqa1GCl-y6f6n0Rm-lu6/s400/notify.png" alt="" id="BLOGGER_PHOTO_ID_5231131139089926498" border="0" /></a><br /><br />This will generate the following output in the console:<br /><br /><br />You can also check the details results of doc/rspec_results.html in your browser:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWB7dFTC_gAzZbCtr1WH4Kncajx7lpDMVT-2sYF4TdWvvsHBWAN55Et6vtUb7v7WyFaCJh2YsWWOWgsmebz9xJCIiXNGd77KLk3tLyMoHZbdrqKit0dDaykuQziK6HNIdwP5ovb3wPieco/s1600-h/results.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWB7dFTC_gAzZbCtr1WH4Kncajx7lpDMVT-2sYF4TdWvvsHBWAN55Et6vtUb7v7WyFaCJh2YsWWOWgsmebz9xJCIiXNGd77KLk3tLyMoHZbdrqKit0dDaykuQziK6HNIdwP5ovb3wPieco/s320/results.png" alt="" id="BLOGGER_PHOTO_ID_5231131532821823938" border="0" /></a>mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com5tag:blogger.com,1999:blog-4229868340014483754.post-25993733776467823532008-07-10T05:24:00.000-07:002008-07-25T17:09:48.269-07:00RSpec: fixtures vs mocksHi there!,<br /><br />It's been a while since the last time I wrote but better late than ever ;)<br />This time I'm going to dissert a little about the way we write our specs. In particular, as I am a rookie with rspec, I would like to know which is the right way to load data into our tests.<br /><br />For that matter, I've found that there are mainly two approaches for that: 1) use fixtures or 2) use mocks/stubs instead.<br /><br /><span style="font-weight: bold;font-size:130%;" >Using Fixtures</span><br /><br />Fixtures enable us to define data for our models in a human readable way using YAML syntax. A simple example, as you may have seen for Test:Unit, is shown below.<br /><br /><span style="font-size:85%;"><span>#spec/fixtures/user.yml</span> <span><br />david:<br /></span> <span>id: 1</span> <span><br />name: David Hasselhof</span> <span><br />birthday: 1500-01-01</span> <span><br />profession: Be-a-god<br />hashed_password: <%= EncryptionLibrary.encrypt("old_password") %></span><span style="font-style: italic;"><br /></span></span><br />Then, in your spec you only have to make reference to the fixture you want to load to make that happened. This approach is very handly for two reasons:<br /><ol><li>This decouples the data to be tested with the specs (tests) itself.</li><li>Is very simple to understand for small bunch of data.<br /></li></ol>But, <span style="font-size:100%;">It has several disadvantages at the time of using it</span>:<br /><ul><li>It works directly with the database which highly degrees the test's performance.</li><li>It is hard to maintain, as it is used by all of your specs (for the description's block that invokes it), and for that: a single change may trigger many errors among different tests.</li><li>It is very implementation dependant, it strongly relies on the model and the current specification of the database's schema. </li></ul>To see the fixtures applied in a practical example, suppose we want to test a chat's application that displays some messages. For this functionality we write the following spec:<br /><code><br />#spec/views/chats/show.html.erb_spec.rb<br />describe "views/chats/show.html.erb" do<br />fixture :chats, :messages<br /><br />before(:each) do<br />@chat = chats(:myChat)<br />render "/chats/show.html.erb"<br />end<br /><br />it "should render attributes in paragraph" do<br />response.should have_text(/MyString/)<br />end<br /><br />it "should render all messages for the chat" do<br />response.should have_tag("tr>td", "Hello!", 1)<br />response.should have_tag("tr>td", "How are you?", 1)<br />response.should have_tag("tr>td", "Excellent!", 1)<br />end<br /><br />end</code><br /><br />With these fixtures:<br /><br /><code>#spec/fixtures/chats.yaml<br />myChat:<br />name: MyString<br />id: 1<br /><br />#spec/fixtures/messages.yaml<br />one:<br />data: Hello<br />chat_id: 1<br />version: 1<br />user_id: 1<br /><br />two:<br />data: How are you?<br />chat_id: 1<br />version: 2<br />user_id: 1<br />three:<br />data: Excellent!<br />chat_id: 1<br />version: 3<br />user_id: 1<br /></code><br /><span style="font-weight: bold;font-size:130%;" >Mocks and stubs way</span><br /><br />The other option is to use mocks and stubs. Although there is a lot of information in the web about it, to help our discussion, I will describe it very shortly. From my experience & research, I understand that the main difference between those two is the following:<br /><a href="http://www.floehopper.org/articles/2006/09/11/the-difference-between-mocks-and-stubs"><br /><span style="font-style: italic;">Stubbing a method is all about replacing the method with code that returns a specified result (or perhaps raises a specified exception). Mocking a method is all about asserting that a method has been called (perhaps with particular parameters).</span><br /></a><br />As you can see, stubbing lets you define methods that are not currently implemented or we decided not to depend on. On the other hand, as we mock objects we specify the behavior that we would expect on the mocked object, making our test more <span style="font-style: italic;">Behavior Driven Development</span>.<br /><br />This is the recommended way of using Rspec as it defines the way the objects must collaborate to accomplish a certain task.<br /><br />Here we show how we implement the chat's example using mocks.<br /><br /><code>#spec/views/chat/show.html.erb_spec.rb<br />describe "/chats/show.html.erb" do<br />include ChatsHelper<br /><br />before(:each) do<br />@chat = mock_model(Chat)<br />@chat.stub!(:name).and_return("MyString")<br />@chat.stub!(:id).and_return(1)<br />@chat.stub!(:to_param).and_return(1)<br />message_1 = mock_model(Message)<br />message_1.stub!(:data).and_return("Hello!")<br />message_1.stub!(:version).and_return(1)<br />message_2 = mock_model(Message)<br />message_2.stub!(:data).and_return("How are you?")<br />message_2.stub!(:version).and_return(2)<br />message_3 = mock_model(Message)<br />message_3.stub!(:data).and_return("Excellent!")<br />message_3.stub!(:version).and_return(3)<br />@messages = [message_1, message_2, message_3]<br />@chat.stub!(:messages).and_return(@messages)<br />@message.stub!(:data)<br />assigns[:chat] = @chat<br />render "/chats/show.html.erb"<br />end<br /><br />it "should render attributes in paragraph" do<br />response.should have_text(/MyString/)<br />end<br /><br />it "should render all messages for the chat" do<br />response.should have_tag("tr>td", "Hello!", 1)<br />response.should have_tag("tr>td", "How are you?", 1)<br />response.should have_tag("tr>td", "Excellent!", 1)<br />end<br /><br />end<br /></code><br />As you can see, the idea behind mocks is to define some behavior that should be part of certain task, and it is very human readable as it uses many features of Rspec mocks.<br /><br /><span style="font-weight: bold;font-size:130%;" >Conclusions</span><br /><br />As we did with fixtures, we have to mention that <span style="font-style: italic;">Mocking</span> has some drawbacks compared with fixtures, such as: it tights the tests with the data they need and forces us to write, perhaps, to much testing code.<br /><br />Despite this, I prefer Mocks/Stubs because I think they provide a very elegant way to work with BDD. I think this way because I find in BDD a solid foundation to agile development, then I choose to prioritize it over the other alternatives. Please, make a comment debate this interesting subject that interests me so much ;)mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com4tag:blogger.com,1999:blog-4229868340014483754.post-68430644765074671232008-03-30T07:13:00.000-07:002008-04-03T18:20:09.718-07:00acts_as_node: visualize the information<div style="text-align: justify;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCOr_u1gC3bQCMB6Gqm-tVlnBBK0UWPJ-kP8LIOqr2if1KxeVd1UZbgKzsjYV8TQn9iInbJJrbHULRqdWIv4hIoNG6EtMlusYgJMVMZ8f4rFlgoMDieqfVEN8jGpZkcpP_W-mMp4bHKGVU/s1600-h/acts_as_node.jpg"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCOr_u1gC3bQCMB6Gqm-tVlnBBK0UWPJ-kP8LIOqr2if1KxeVd1UZbgKzsjYV8TQn9iInbJJrbHULRqdWIv4hIoNG6EtMlusYgJMVMZ8f4rFlgoMDieqfVEN8jGpZkcpP_W-mMp4bHKGVU/s400/acts_as_node.jpg" alt="" id="BLOGGER_PHOTO_ID_5183939713507371698" border="0" /></a><br /></div><br /><br />Hello everyone,<br /><br />Once and again, I've been occuping my time coding another plugin, this one, I got to tell, I really enjoy doing it.<br /><br />Why ? Because I love the topic: "Information Visualization", and this is the very purpose for this plugin, to help you, or your clients, to see the data in graphic form in the way it can help you to inferred information using the perceptual senses.<br /><br />acts_as_node plugin allows you to see the relationships between specific instances of the application models as it renders a graph drawing based on that information.<br /><br />It is very straighforward to use, because you don't have to modify any model or controller, instead you just need to use the <span style="font-weight: bold; font-style: italic;">graph_drawimg</span> helper that makes all the magic within the view.<br /><br /><span style="font-size:180%;">Installation</span><br /><br />First of all, you got to download the plugin in the following way:<br /><br /><span style="font-style: italic;">./script/plugin install http://acts-as-node.googlecode.com/svn/trunk/acts_as_node</span><br /><br />Then, the plugin needs to generate the necesesary files and assets with the generate command:<br /><br /><span style="font-style: italic;">./script/generate acts_as_node</span><br /><br />Finally, we got to define a route for the plugin, that will be used to generate an intermediate xml file which is the input for the graph drawing algorithm. The route should look like this:<br /><br />#config/routes.rb<br /><span style="font-style: italic;">map.graph_generator '/graph_generator', :controller => "acts_as_node", :action => "generate_graph"</span><br /><br />That's all we need to get started. Now we can just try it out!<br /><br /><span style="font-size:180%;">Usage</span><br /><br />First of all, you have to add the following line to the application's layout file (or where you want to use it):<br /><br />#app/views/layout/application.rhtml<br />..<br /><span style="font-style: italic;"><%= javascript_include_tag :acts_as_node %></span><br />..<br /><br />Then, all you have to do is use the <span style="font-style: italic;">graph_drawing </span>helper. For that matter, I am going to explain the graph_drawing helper signature to understand how it works:<br /><br /><span style="font-weight: bold;">graph_drawing </span><span style="font-style: italic; font-weight: bold;">class_name, root_id, optional_parameters</span><br /><br />The parameters represents:<br /><ul><li><span style="font-style: italic;">class_name</span>. The model name of the root of the graph. (Eg. <span style="font-style: italic;">:ProductCategory</span>)<br /></li><li><span style="font-style: italic;">root_id</span>. The id of root instance that will generate the graph (Eg: <span style="font-style: italic;">1</span>)<br /></li></ul>There is a long list of <span style="font-style: italic;">optional_parameters</span>, that I'll describe here:<br /><ul><li><span style="font-style: italic;">depth</span>. The depth of the graph involved, counting from the root node.<br /></li><li><span style="font-style: italic;">layout</span>. This enable us to set which layout we want to render the graph. The available options are: <span style="font-style: italic;">:SnowflakeLayout </span>& :<span style="font-style: italic;">ForceDirected</span><span style="font-style: italic;">Layout</span>.</li><li><span style="font-style: italic;">content</span>. The DOM elment's id that will contain the drawing. If this value is not set, it will be add to document.body object.<br /></li><li><span style="font-style: italic;">childRadius</span>. The lenght of the edge between nodes</li><li><span style="font-style: italic;">fanAngle</span>. The angle between silbling nodes, applies only for the Snowflake layout.</li><li><span style="font-style: italic;">mass</span>. This value represents the mass of each node for the algorithm, it is used to calculate the forces & the separation between the nodes, applies only for ForceDirected layout (default value: 0.3)<br /></li><li><span style="font-style: italic;">children_accesors</span>. This is an important parameter, it indicates which methods of the model we will invoked to inspect for children nodes. It MUST be defined as a hash like the following: { :ProductCategory => [:children, :compaines] }. In this example, the graph_drawing helper will inspect the <span style="font-style: italic;">children's </span>and <span style="font-style: italic;">companies's </span><span style="font-style: italic;"></span>collections <span style="font-style: italic;"> </span>of the ProductCategory instances that the algorithm reaches.<br /></li><li><span style="font-style: italic;">children_fields</span>. This enables the algorithm to set the appropiate title, content & picture attributes of the nodes. For example, in the followin definition for <span style="font-style: italic;">children_fields =></span> <span style="font-style: italic;">{ :ProductCategory => { :title => :name, :content => :description, :picture => :picture_uri}</span>, the graph_drawing helper will render all ProductCategory nodes with a tooltip that contains a title assosiated with the <span style="font-style: italic;">name </span>field of the instance, the content of the tooltip will be the value of <span style="font-style: italic;">description </span>field of the instance, and the node itself will be rendered with the image provided by the picture_uri field.</li><li><span style="font-style: italic;">image_width</span>. This parameter enables you to set the width of the image for the pictures of the nodes.</li><li><span style="font-style: italic;">image_height</span>. Likewise The same concept of image_width related to the height of the pictures.<br /></li></ul>The best way to get started is to see examples:<br /><br />#sample.rhtml<br /><div id="graph_id"></div><br />..<br /><%= graph_drawing :NetworkNode, 1, :container => "$('graph_id')", :children_fields => { :NetworkNode => {:title => :name, :content => :descrption, :picture => 'picture' }} %><br />..<br /><br />This configuration generates an output similiar to the picture shown at the beginning of the article. In that case, the algorithm will start with the object NetworkNode.find(1), provided that the <span style="font-style: italic;">class_name </span>parameter is <span style="font-style: italic;">:NetworkNode</span> and the root_id is 1. The drawing will be displayed within the DOM element with 'graph_id' identificator. The values used to display the attributes of the tooltip are :name (for the header), and :content (for the body). The image is obtained by invoking the picture method of the instance. It is worth noticing that <span style="font-style: italic;">:children_accesors</span> are not defined, this way the helper assumes :children method is present (this will be the case if the model uses acts_as_tree, acts_as_nested, among other well known plugins).<br /><br />Another sample, a little bit more comlex:<br /><br /><%= graph_drawing :ProductCategory, 1, :layout => :<span>ForceDirectedLayout</span>, :mass => 0.3, :container => "$('graph_id')", :children_accessors => { :ProductCategory => [:children, :companies, :legal_questions], :Company => [], :LegalQuestion => [:legal_categories], :LegalCategory => []}, :children_fields => { :ProductCategory => {:title => :name, :picture => 'picture', :content => :name}, :Company => {:title => :name, :content => :name}, :LegalQuestion => {:title => :title, :content => :answer_html}, :LegalCategory => {:title => :name, :content => :name}} %><br /><br />In this example, the graph will be generated starting from a ProductCategory instance (the one with id = 1). And it will be asked for the following collections: <span style="font-style: italic;">children, companies, legal_questions.</span> Then for each instance that the algorithm reaches, it will look up in the children_accesors hash to see what collection it has to cover. For example, when it reach a LegalQuestion instance, it will only process the <span style="font-style: italic;">legal_categories </span>collection (as it is the only collection name defined for <span style="font-style: italic;">:LegalQuestion</span> in the hash).<br /><br /><span style="font-size:180%;">Improvment & Comments</span><br /><span style="font-size:100%;"><br />If you have any comments, questions, and/or suggestions please don’t hesitate to send me an email <a href="mailto:actsasnode@gmail.com">here</a>.</span>mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com1tag:blogger.com,1999:blog-4229868340014483754.post-72903466805824916102008-01-15T08:41:00.000-08:002008-10-13T03:39:55.775-07:00Active_poll PluginFinally, I am glad to announce that I have released the active_poll plugin, which by the way, is my first Ruby On Rails plugin. This plugin is intended to provide poll functionality to applications that register the votes associated with users or, if not desired, just count the votes for anonymous participants.<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">Install plugin</span></span><br /><br />To install it, you have to run the following command:<br /><br /><pre>script/plugin install http://active-poll.googlecode.com/svn/trunk/active_poll<br /><br /></pre>This command will download the plugin code and run a script. The next step is run the generator command:<br /><pre>./script/generate active_poll<br /></pre><br />This command will run a script that will prompt you which is the name of the model you want to associate with a user vote (typically the <span style="font-style: italic;">User </span>model). If you don't want to track which users made the votes for a particular<br />poll, you just can configure the poll as anonymous, this way, the<br />plugin just increments a counter for each selected answer.<br /><br />This will copy the plugin's files and the corresponding migration into your project. As usual, it means that you will need to run:<br /><br /><pre>rake db:migrate</pre><br /><span style="font-size:130%;"><span style="font-weight: bold;">Create poll</span></span><br /><br />At this point, we got our plugin properly installed. Now, in order to start using it, we got to create our first poll, with the following command:<br /><pre><br />script/runner vendor/plugins/active_poll/create_poll.rb<br /><br /></pre>The script is intended to configure the poll. It will ask you which are the answers, which is the questions displayed, among other configurations (allows multiple selection, who is allowed to vote: registered users, anonymous, etc.) for the poll.<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">Show poll in views</span></span><br /><br />For us to see the poll <span style="font-style: italic;">up & running</span> in our application there are three things left to do:<br /><ol><li><span style="font-style: italic;">Use View helper</span>. Insert the active_poll helper within the views that we want to show the poll, as it is shown below:</li><pre><br />#someview.rhtml (where you want to show the poll)..<br />< %= <span style="font-style: italic; color: rgb(255, 0, 0); font-weight: bold;">active_poll</span> ( poll_name, { :in_place => true, :redirect_to => <span style="font-style: italic;">some_url</span>, :view_dir => <span style="font-style: italic;">view_directory</span> }<br />) %><br />..<br /></pre>Where:<br /><ul><br /><li><span style="font-style: italic;">poll_name</span>. This is the name that identifies the poll to display. This is the only required parameter.<br /></li><br /><li><span style="font-style: italic;">:in_place</span>. Enables AJAX functionality to the poll if it is set to true, otherwise the whole page would be reload while submitting a vote.<br /></li><br /><li><span style="font-style: italic;">:redirect_to</span>. This is a url to redirect after the vote.<br /></li><br /><li><span style="font-style: italic;">:view_dir</span>. If present, it loads the poll pages (show_poll, already_vote, user_not_logged, etc) from the directory set by this parameter. Otherwise, all the active_poll pages would be take from the <span style="font-style: italic;">views </span>directory inside the active_poll plugin's folder.<br /></li></ul><br /><li><span style="font-style: italic;">Insert act_as_vote_handler statement</span>. You have to append <span style="font-style: italic;">act_as_vote_handler </span>statement (highlighted with red in the code below) to the controller that handles the view (where the poll is displayed). Here we can see an example:<br /><div style="text-align: left;"><pre><br />class SomeController<br /><span style="font-style: italic;font-family:mon;" >...<br /><span style="font-weight: bold;"> <span style="color: rgb(255, 0, 0);">acts_as_vote_handler</span><br /></span>..<br />end</span><br /></pre><br /></div></li><li><span style="font-style: italic;font-family:mon;" >Modify routes.rb accordingly. </span><span style="font-family:mon;">The acts_as_vote_handler hook defines the ap_vote_registered method which effectivity process the votes. For that matter, we have to enable the route for that method on this controller, which would look like this:</span><br /><br /><pre><span style="font-family:mon;">#routes.rb</span><br /><span style="font-family:mon;">map.connect 'somecontroller/ap_vote_registered', :controller => 'some', :action => 'ap_vote_registered'</span><br /></pre></li><br /></ol><span style="font-size:130%;"><span style="font-weight: bold;">Views of active_poll</span></span><br /><br />This plugin contains multiple views, the one that would be rendered (using the helper mentioned in the section above) depends of the circumstances. We describe each one of them here:<br /><br /><ul><li><span style="font-style: italic;">after_vote.rhtml</span>. Shows a message after the user votes.<br /></li><li><span style="font-style: italic;">already_voted.rhtml</span>. Shown when the plugin finds out that you already (by means of database for logged users or cookies for anonymous) voted for the current poll.<br /></li><li><span style="font-style: italic;">error.rhtml</span>. This view is rendered by means of an internal error (poll delete while someone is voting, for example)<span style="color: rgb(51, 51, 255);"></span><br /></li><li><span style="font-style: italic;">max_votes_exceeded.rhtml</span>. When the user chooses more options that is allowed in the poll's configuration it shows this view.<br /></li><li><span style="font-style: italic;">no_vote.rhtml</span>. This is rendered when the user press "Vote" but he/she doesn't select any option.<span style="color: rgb(51, 51, 255);"></span><br /></li><li><span style="font-style: italic;">poll_not_found.rhtml</span>. Polls are identified by the name, defined with at the time we run the create_poll.rb script. When we use a the active_poll helper with a poll's name that is not defined in the database we show the poll_not_found.rhtml view.<br /></li><li><span style="font-style: italic;">poll_outdated.rhtml</span>. This view is rendered after the end_date (date of finalization) of the poll is reached.<span style="color: rgb(51, 102, 255);"></span></li><li><span style="font-style: italic;">show_poll.rhtml</span>. Displays the poll itself, with the appropriate form that submits the vote.<br /></li><li><span style="font-style: italic;">show_results.rhtml</span>. This view shows the current results of the poll.</li><li><span style="font-style: italic;">user_not_logged.rhtml</span>. It is displayed when the poll is configured to logged users only and the user is not logged in.<span style="color: rgb(51, 51, 255);"></span><br /></li></ul>For aesthetic purposes, you should redefine all the views mentioned above in a new directory (accessed by means of the <span style="font-style: italic;">:view_dir</span> parameter) contained in your application. Also, you should base your customized views in the templates that comes in the <span style="font-style: italic;">views </span>directory, where it is shown how to pass the parameters to the controller, for example.<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">Check for logged users</span></span><br /><br />This plugin realizes that the current user is logged in by asking the session for the user_id, in this way:<br /><br /><span style="font-style: italic;">user_model_id</span> = session[:<span style="font-style: italic;">user_model</span><span style="font-style: italic;">_id</span>]<br /><br />If this value is set, then it is assumed that the user is logged, otherwise the user is treated as anonymous.<br /><br />In particular, this methodology integrates transparently with the restful_authentication which uses the same strategy behind the scenes.<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">Improvment & Comments<br /><br /></span></span> If you have any comments, questions, and/or suggestions please don’t hesitate to send me an email <a href="mailto:active.poll@gmail.com">here</a>.mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com24tag:blogger.com,1999:blog-4229868340014483754.post-63822397046666955992007-12-19T08:42:00.000-08:002008-01-22T08:03:02.080-08:00multimodel search with acts_as_ferret<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK-rUmlk8dD4Aced6ZSrUPPE-1GbQlH3vk0-oyN-VcFUN2_ekoqI08LLxPsJ_92xl0ZyEMttfwulwpT3cG0xNA7nK5WeYjMEFCHOb0cP0zQGq5vxUIDFZvHxpEUuRG0Zgm6CmOgue9i1Sh/s1600-h/logo-ferret.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK-rUmlk8dD4Aced6ZSrUPPE-1GbQlH3vk0-oyN-VcFUN2_ekoqI08LLxPsJ_92xl0ZyEMttfwulwpT3cG0xNA7nK5WeYjMEFCHOb0cP0zQGq5vxUIDFZvHxpEUuRG0Zgm6CmOgue9i1Sh/s320/logo-ferret.png" alt="" id="BLOGGER_PHOTO_ID_5146509790194759970" border="0" /></a>Hi everyone!,<br /><div style="text-align: left;"><br /></div><div style="text-align: left;">First of all, I want to introduce myself to you people. I'm Marcelo Giorgi an Uruguayan developer who has a strong background in web technologies (mainly using J2EE and PHP), but lately I found myself very excited studiyng Ruby and RoR, why? I just can't explaing it, it was love at first sight ^_^.<br /></div><br />I want to share with you how you can use acts_as_ferret plugin, for Ruby On Rails, with multiple models (http://projects.jkraemer.net/acts_as_ferret/), such an excellent search engine. I had to research a bit to keep things up and running, hopefully it could help someone...<br /><br /><span style="font-size:130%;">Install acts_as_ferret<br /><br /></span>The installation can be accomplished by executing the following line on the rails project:<br /><span style="font-family:monospace;"><br /></span><span style="font-style: italic;">script/plugin install svn://projects.jkraemer.net/</span><span style="font-style: italic;" class="searchword0">acts_as_ferret</span><span style="font-style: italic;">/tags/stable/</span><span style="font-style: italic;" class="searchword0">acts_as_ferret<br /><br /></span><span class="searchword0">And that's it, now we can start messing around with it!</span><span style="font-style: italic;" class="searchword0"><br /></span><br /><span style="font-size:130%;">Use single model for a query<br /><br /></span>Before showing some complex query, I'll be showing the basic usage by example. Suppose you want to make a search through an Article model. To make this model <span style="font-style: italic;">searcheable</span> we got to invoke the <span style="font-style: italic;">acts_as_ferret</span> helper within the class definition, as shown:<br /><br />article.rb<br /><span style="font-style: italic;">class Article < style="font-style: italic;"> .. </span><br /><span style="font-style: italic;"> acts_as_ferret </span><br /><span style="font-style: italic;"> .. </span><br /><span style="font-style: italic;">end </span><br /><br /></span><span>Now, we simple need to define a new controller that make use of the new search feature, we named this controller as SearchEngineController:</span><span style="font-style: italic;"><br /><br />search_engine_controller.rb<br />class SearchEngineController < collection =" Article.<span class="nfakPe">find_with_ferret</span> params[:q], :page => params[:page]<br /> end<br />end<br /><br /></span><span>Finally, we just need to modify the view to show the results:</span><span style="font-style: italic;"><br /><br />app/views/search_engine_controller/index.rhtml<br />...<br /><br /></span><span>As you might noticed, we used will_paginate helper, that enables us paginate the results provided by acts_as_ferret.</span><span style="font-style: italic;"><br /><br /><span style="font-size:130%;">Use many models for a query<br /><br /></span></span><span>Until this point, all seems very straighforwad (didn't it?). The documentation of find_with_ferret indicates that we simply code <span>:store_class_name => :true</span> to enable search over multiple models. Then, we only have to use find_with_ferret with the appropiate paramters to indacate in which models we want to search, this can be done with this:</span><span style="font-style: italic;"><br /><br />search_engine_controller.rb<br /><span style="font-style: italic;">class SearchEngineController < style="font-style: italic;"> def index<br /></span><i> @collection = Article.<span class="nfakPe">find_with_ferret</span> params[:q], :page => params[:page], :multi => [NewsItem]</i><br /><span style="font-style: italic;"> end </span><br /><span style="font-style: italic;">end </span><br /></span></span><span><span><br /></span></span><span><span>This code mandates that the search must be perfomed through the Article and NewsItem's models. Everything seems right, but...<br /><br />But, when we run it, we get the following error:</span></span><span style="font-style: italic;"><span style="font-style: italic;"><br /><br /><i>RuntimeError in Search resultController#index<br />':store_class_name => true' required for multi_search to work<br /><br /></i></span></span><span><span>The problem is that multisearh requires us, to create the index over the models we will use in the search. This can be accomplished with a rake task. As the below code suggest.<br /></span></span><span style="font-style: italic;"><span style="font-style: italic;"><br />lib/task/ferret.rake<br /><pre class="wiki"><span style="font-style: italic;">namespace :ferret do</span><br /><span style="font-style: italic;">desc "Rebuild all Indexes"</span><br /><span style="font-style: italic;">task :rebuild_all_indexes => [:environment] do</span><br /><span style="font-style: italic;">%w(Article NewsItem).each { |s| s.constantize.rebuild_index }</span><br /><span style="font-style: italic;">end</span><br /><span style="font-style: italic;">end</span><br /></pre></span></span>Then, we just got to run the task 'rebuild_all_indexes' before we start the server and everthing should work just fine.mgiorgihttp://www.blogger.com/profile/00085877078962198098noreply@blogger.com5