The Dev Blog

Putting Family Management on Rails!

A World Time Server In One Line Of Rails

Posted by Guy Naor Thu, 09 Nov 2006 15:26:00 GMT

First, sorry for the delay in posting more on the XMPP/Jabber serie. I was a bit busy. So until I write the next installment, here's a small piece of rails coolness.

Want to know the exact time anywhere in the world? Including daylight saving taken into account? How about doing it in one line of rails code?

  1. Create a new rails application: rails .

  2. Install the tzinfo plugin: script/plugin install tzinfo_timezone

  3. Edit app/controllers/application.rb

  4. Add to it the following method (ok, it's 3 lines if we count the def and the end and not try to squeeze it into one with ; ):

def get_time
   render :text => TzinfoTimezone[params[:id]].utc_to_local(Time.now.getutc).strftime('%Y-%m-%d %H:%M:%S') rescue render :text => "ERROR - check your time zone", :status => 500
end

Now run it: script/server

Browse to: http://localhost:3000/application/get_time/Tokyo or http://localhost:3000/application/get_time/London. Check out the tzinfo plugin for the names of the supported time-zones. And you can add more to the mapping there.

You can improve performance a bit by turning sessions off completely. Do that either in the configuration or by adding session :off to the application controller class.

If you keep your computer clock accurate with ntp, you will get a pretty accurate time. The request is processed at a really high speed, so that shouldn't be a problem. And if the round trip to the server and back is quick, you will have a 1 second accuracy. Not bad for one line of code.

Posted in ,  | no comments

del.icio.us:A World Time Server In One Line Of Rails digg:A World Time Server In One Line Of Rails spurl:A World Time Server In One Line Of Rails wists:A World Time Server In One Line Of Rails simpy:A World Time Server In One Line Of Rails newsvine:A World Time Server In One Line Of Rails blinklist:A World Time Server In One Line Of Rails furl:A World Time Server In One Line Of Rails reddit:A World Time Server In One Line Of Rails fark:A World Time Server In One Line Of Rails blogmarks:A World Time Server In One Line Of Rails Y!:A World Time Server In One Line Of Rails smarking:A World Time Server In One Line Of Rails magnolia:A World Time Server In One Line Of Rails segnalo:A World Time Server In One Line Of Rails

Ruby and XMPP/Jabber - a Multi Posts Series, Part 1

Posted by Guy Naor Tue, 10 Oct 2006 20:24:00 GMT

Instant Messages is one of the most common apps running on people's desktops. And they have the nice feature of popping up when a new message comes in. Exploiting this programmatically can open up a huge array of options.

In Famundo we added the ability to send alarms notifications to IM addresses. The initial implementation was easy and pretty trivial, given all the samples I found. When I wanted to go further, it became complicated, and there were no more examples to look at. Things like sending rich text messages, or talking to other IM services through XMPP gateways. I promise to show how to just that (and other things) in this series of posts.

I started with Jabber4R, but then realized it's not supported anymore, and all development is now done in XMPP4R. So I switched to it. It is a much more advanced library, and is pretty big. One of the prolems with it is the complexity. Part of it is the inherent complexity of the XMPP protocol. (Though I read somewhere it's simple, don't believe everything you read...)

My goal in this series of posts is to guide you into using XMPP4R to send messages, subscribe to services, send queries, etc... I might get also into receiving messages, though I see it as less important for rails applications. We'll see how much time I have on my hands. If there's something specific you would like me to discuss - let me know.

So lets get starting!

XMPP is a messaging protocol based on XML messages passed between clients and servers. Better known as THE open source for instant messaging protocol/server, is also the protocol used by GoogleTalk. there are also public servers you can subscribe to and use freely. With the right gateways it will let you send messages to all the other major IM networks (AIM, ICQ, MSN and YM). You can read all about it here. The protocol is described in a large collection of RFCs/JEPs and as those usually are, it's a bit of work to decipher them. But I do recommend reading atleast some of them. Depending on what you are trying to achieve.

Another good option are the Jabber/XMPP related books. But be warned they are not that complete, and are not Ruby based. The two I have are Java based:

Programming Jabber

Instant Messaging in Java

The following installments will be:

Part 2: Logging in and sending simple messages to a Jabber/XMPP server

Part 3: Adding html to the messages

Part 4: Queries + Callbacks

Part 5: Presence, or here I am!

Part 6: Sending messages to proprietary networks (AIM, ICQ, MSN, YM)

Tune in for more....

Posted in , ,  | no comments | no trackbacks

del.icio.us:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 digg:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 spurl:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 wists:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 simpy:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 newsvine:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 blinklist:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 furl:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 reddit:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 fark:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 blogmarks:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 Y!:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 smarking:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 magnolia:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1 segnalo:Ruby and XMPP/Jabber - a Multi Posts Series, Part 1

Deploying to Staging and Production with Capistrano

Posted by Guy Naor Thu, 28 Sep 2006 17:07:30 GMT

After an application is released, deployment should be in stages. To a test server, a staging server and a production one. I hope you have a staging server...

But capistrano has no knowledge of the difference between a staging and a production server. To make it easy to deploy using capistrano, a small change is needed to deploy.rb.

We add an if statement based off of an environment variable, and with it select the servers to deploy to. I also use this same variable to protect my production servers from accidental data changes, by adding an if to all data altering recipies I use only in staging (like reloading the DB and such). This way there's no way to run those commands on the production server.

Now, to deploy on staging: env DEPLOY='STAGING' cap deploy

And on production: env DEPLOY='PRODUCTION' cap deploy

Here's a small snippet of code with the change:

if ENV['DEPLOY'] == 'PRODUCTION'
   puts "*** Deploying to the \033[1;41m  PRODUCTION  \033[0m servers!"
   role :web, "www.your.domain.com"
   role :app, "app.your.domain.com
   role :db,  "db.your.domain.com", :primary => true
else
   puts "*** Deploying to the \033[1;42m  STAGING  \033[0m server!"
   role :web, "www.staging.domain.com"
   role :app, "app.staging.domain.com
   role :db,  "db.staging.domain.com", :primary => true
end

Posted in  | 4 comments | no trackbacks

del.icio.us:Deploying to Staging and Production with Capistrano digg:Deploying to Staging and Production with Capistrano spurl:Deploying to Staging and Production with Capistrano wists:Deploying to Staging and Production with Capistrano simpy:Deploying to Staging and Production with Capistrano newsvine:Deploying to Staging and Production with Capistrano blinklist:Deploying to Staging and Production with Capistrano furl:Deploying to Staging and Production with Capistrano reddit:Deploying to Staging and Production with Capistrano fark:Deploying to Staging and Production with Capistrano blogmarks:Deploying to Staging and Production with Capistrano Y!:Deploying to Staging and Production with Capistrano smarking:Deploying to Staging and Production with Capistrano magnolia:Deploying to Staging and Production with Capistrano segnalo:Deploying to Staging and Production with Capistrano

Improving the Rails Logger

Posted by Guy Naor Fri, 22 Sep 2006 20:02:00 GMT

The rails logger is VERY useful. It amazes me that I don't really feel I need a debugger in rails (after using one for years in Visual C++) and a lot of it is the console and the logger.

One thing missing from the logger, is the process id of the rails instance that is logging. This is important when you have more than one instance of your app serving requests through fcgi or mongrel or whatever. It's needed in order to enable us to see how the request was handled, as we can extract only logs that pertain to this specific request, even when it's mixed with other requests.

Using the fact that all classes in Ruby are open, we modify the logger to do what we need. Either create a file in your lib directory, or anywhere else you prefer, and in your config/environment.rb add the line:

require 'pid_logger'

The file should include the following (most of it extracted from the default rails logger):

class Logger
  private
    # Ruby 1.8.3 transposed the msg and progname arguments to format_message.
    # We can't test RUBY_VERSION because some distributions don't keep Ruby
    # and its standard library in sync, leading to installations of Ruby 1.8.2
    # with Logger from 1.8.3 and vice versa.
    if method_defined?(:formatter=)
      log_str = ""
      def format_message(severity, timestamp, progname, msg)
        log_str = "[#{$$}][#{severity[0...1]}] msg\n" # This is the only real change... Added a pid
      end
    else
      def format_message(severity, timestamp, msg, progname)
        log_str = "[#{$$}][#{severity[0...1]}] msg\n" # This is the only real change... Added a pid
      end
    end
  end

One more possible change that I use in some applications, is to color the background of debug messages I sent to the logger. The change here is also very slight. Here is the same file, but with the added coloring of the output with a green or red background depending on severity, when a line start with 4 or more > + - = characters (e.g: ">>>>>>>> This is a test" will have the >>>>>>>> part with a red backround if error, and green otherwize). It makes finding the log messages very eash.

class Logger
  private
    # Ruby 1.8.3 transposed the msg and progname arguments to format_message.
    # We can't test RUBY_VERSION because some distributions don't keep Ruby
    # and its standard library in sync, leading to installations of Ruby 1.8.2
    # with Logger from 1.8.3 and vice versa.
    if method_defined?(:formatter=)
      log_str = ""
      def format_message(severity, timestamp, progname, msg)
        log_str = "[#{$$}][#{severity[0...1]}] #{color_special_msg(msg, severity)}\n" # This is the only real change... Added a pid
      end
    else
      def format_message(severity, timestamp, msg, progname)
        log_str = "[#{$$}][#{severity[0...1]}] #{color_special_msg(msg, severity)}\n" # This is the only real change... Added a pid
      end
    end

    def color_special_msg msg, severity  
      # A small trick to get highlighting of the messages if they are our logged messages
      # that start with a long sequence of >>> or *****
      sub_color = severity[0...1] == 'E' ? '41' : '42' # Red for errors, green otherwize
      msg.sub(/^((\*|>|\+|-|=){4,})/, "\033[1;#{sub_color}m\\1\033[0m")     
    end

end

And as you can see, adding whatever changes you want to the logger is easy. So go ahead and make it work the way YOU want it to.

Posted in ,  | no comments | no trackbacks

del.icio.us:Improving the Rails Logger digg:Improving the Rails Logger spurl:Improving the Rails Logger wists:Improving the Rails Logger simpy:Improving the Rails Logger newsvine:Improving the Rails Logger blinklist:Improving the Rails Logger furl:Improving the Rails Logger reddit:Improving the Rails Logger fark:Improving the Rails Logger blogmarks:Improving the Rails Logger Y!:Improving the Rails Logger smarking:Improving the Rails Logger magnolia:Improving the Rails Logger segnalo:Improving the Rails Logger

To AJAX Or Not To AJAX That Is The Question

Posted by Guy Naor Fri, 15 Sep 2006 02:45:05 GMT

I'm sure this would have been Hamlet's question has he been with us today working as a web developer.

This subject was beaten to death already, but just a few days ago I was presented with just that decision, and it made it really clear in my mind. In my case the decision was simple, but I know it's not always the case. So having reference points might help others.

While developing the help system for Famundo that I mentioned yesterday, the admin part was developed with Ajax Scaffold and is very very nice and useable. When I did the public face of the help system, I continued with a full Ajax solution. The table of content, the internal linking and the tagging mechanism, all updated the main view using Ajax.

It was cool, it was nice, but it was a disaster as far as useability goes. You can't go forward and backward betweeb pages you've read and it makes navigation a nightmare for the user. It took me a few minutes of really trying to use it to figure this part really doesn't work well with Ajax. So instead of that I made everything into regular links, added VERY aggressive caching and it's much better now. The only thing I left with Ajax is the search which makes sense to do this way.

My conclusion from that is that if navigation is a central part of the functionality - don't do Ajax. Just use standard and expected navigation. It's more intutive and easier to use.

For editng/updating heavy interfaces, Ajax makes a lot of sense.

Posted in  | no comments | no trackbacks

del.icio.us:To AJAX Or Not To AJAX That Is The Question digg:To AJAX Or Not To AJAX That Is The Question spurl:To AJAX Or Not To AJAX That Is The Question wists:To AJAX Or Not To AJAX That Is The Question simpy:To AJAX Or Not To AJAX That Is The Question newsvine:To AJAX Or Not To AJAX That Is The Question blinklist:To AJAX Or Not To AJAX That Is The Question furl:To AJAX Or Not To AJAX That Is The Question reddit:To AJAX Or Not To AJAX That Is The Question fark:To AJAX Or Not To AJAX That Is The Question blogmarks:To AJAX Or Not To AJAX That Is The Question Y!:To AJAX Or Not To AJAX That Is The Question smarking:To AJAX Or Not To AJAX That Is The Question magnolia:To AJAX Or Not To AJAX That Is The Question segnalo:To AJAX Or Not To AJAX That Is The Question

Ajax and Caching

Posted by Guy Naor Tue, 12 Sep 2006 17:30:18 GMT

Sorry I didn't write for so long... Too busy working, I guess.

I wrote a tiny help system for Famundo. It's like a VERY oppinionated CMS ;-).

It's wiki like for entering the help pages, and uses caching for serving out the pages to the users. I'll have more details about, and if anyone is interested, we have plans for open sourcing it.

I was using some Ajax to display the pages, but then discovered that Ajax and caching are not too friendly to each other.

Caching in rails works only for GET out of the box, and Ajax calls are POST by default. So I changed that to POST using :method => :get in link_to_remote.

But this brings a new problem - any AJAX call that uses RJS templates, or the

render :update do |page|

end

methods will fail with page caching, as the Ajax caller expects the returned content to be of type text/javascript, but after being cached to a page, the content type returned by the server will be text/html, and the Ajax listener won't handle it correctly.

The option we have for handling this is either to find a way for the webserver server to serve different file (and we will have to save the files with the correct extension), or use fragment caching.

It works perfectly with fragments, but then caching isn't as aggressive, because for every call the rails process still needs to be called.

Posted in ,  | no comments | no trackbacks

del.icio.us:Ajax and Caching digg:Ajax and Caching spurl:Ajax and Caching wists:Ajax and Caching simpy:Ajax and Caching newsvine:Ajax and Caching blinklist:Ajax and Caching furl:Ajax and Caching reddit:Ajax and Caching fark:Ajax and Caching blogmarks:Ajax and Caching Y!:Ajax and Caching smarking:Ajax and Caching magnolia:Ajax and Caching segnalo:Ajax and Caching

Rotating the Rails logs with FastCGI

Posted by Guy Naor Fri, 23 Jun 2006 15:47:00 GMT

Deploying a rails app will eventually lead to the need to manage the rails log files. They get pretty big fast, and if the log level also include the SQL queries, they grow EXTREMLY fast.

Though there is some information about it in the Rails Wiki, it's missing restarting the FastCGI processes. If you are using dynamicaly launched FastCGI processes from lighty or apache, the web server log rotation will restart the FastCGI processes and you will be covered. But if you are using FastCGI processes you launch (using spinner/spawner or a similar solution) the log file will be rotated, but the newly created log file won't be used by the ruby app until it is restarted.

Here is my logrotate configuration:

# Assume the the rails app is installed to /app/my_blog/current
# And that the user we running the app with is called rails
/app/my_blog/current/log/*.log {
  daily
  missingok
  rotate 7
  compress
  delaycompress
  notifempty
  create 0660 rails rails
  postrotate
     /app/my_blog/current/script/process/reaper -a graceful -d /app/my_blog/current/public/dispatch.fcgi 1>/dev/null 2>&1 || true
  endscript
}

The main change from the rails wiki version is the postrotate call that causes the fastCGI processes to restart and reopen the log file.

Posted in , ,  | no comments | no trackbacks

del.icio.us:Rotating the Rails logs with FastCGI digg:Rotating the Rails logs with FastCGI spurl:Rotating the Rails logs with FastCGI wists:Rotating the Rails logs with FastCGI simpy:Rotating the Rails logs with FastCGI newsvine:Rotating the Rails logs with FastCGI blinklist:Rotating the Rails logs with FastCGI furl:Rotating the Rails logs with FastCGI reddit:Rotating the Rails logs with FastCGI fark:Rotating the Rails logs with FastCGI blogmarks:Rotating the Rails logs with FastCGI Y!:Rotating the Rails logs with FastCGI smarking:Rotating the Rails logs with FastCGI magnolia:Rotating the Rails logs with FastCGI segnalo:Rotating the Rails logs with FastCGI

Fixing Rails for Postgres Schemas

Posted by Guy Naor Sat, 03 Jun 2006 22:26:00 GMT

Postgres has a very powerful feature in the schemas, especially with the schema search_path. Using it I can have multiple separate families, all using the same database connection, but also fully separated (with a different user to access the schema files). Using the search path and the SESSION AUTHORIZATION it is completely transparent to rails. I'll have a later post explaining how to use this technique in a rails application.

The cool thing about the search_path is that you can have multiple instances of the same table in different schemas, and using SET SESSION AUTHORIZATION, you can see a different table each time. Rails works great with this.

But there's a small bug in the postgres driver for handling sequences. (The bug doesn't show up in development mode because the objects are reloaded all the time.) When postgres loads the sequence name used for the auto increment keys (serial type in postgres), it adds the schema to the sequence name. So instead of messages_id_seq, it will return public.messages_id_seq. If another request coming in, tries to insert a record, the call to SELECT currval() will return the wrong sequence, and if (like I do) there's user security between schemas, you get an access error and the insert fails.

The fix is very simple - just return the unqualified sequence name, as the rest of the access in rails is unqualified.

I'm posting a ticket with the fix and a test file for it. The fix to the rails edge is the following (in diff format):

Index: lib/active_record/connection_adapters/postgresql_adapter.rb
===================================================================
--- lib/active_record/connection_adapters/postgresql_adapter.rb (revision 4414)
+++ lib/active_record/connection_adapters/postgresql_adapter.rb (working copy)
@@ -296,8 +296,9 @@
               AND def.adsrc ~* 'nextval'
           end_sql
         end
-        # check for existence of . in sequence name as in public.foo_sequence.  if it does not exist, join the current namespace
-        result.last['.'] ? [result.first, result.last] : [result.first, "#{result[1]}.#{result[2]}"]
+        # check for existence of . in sequence name as in public.foo_sequence.  if it does not exist, return unqualified sequence
+        # We cannot qualify unqualified sequences, as rails doesn't qualify any table access, using the search path
+        [result.first, result.last]
       rescue
         nil
       end
                                                                                                                                

Posted in ,  | no comments | no trackbacks

del.icio.us:Fixing Rails for Postgres Schemas digg:Fixing Rails for Postgres Schemas spurl:Fixing Rails for Postgres Schemas wists:Fixing Rails for Postgres Schemas simpy:Fixing Rails for Postgres Schemas newsvine:Fixing Rails for Postgres Schemas blinklist:Fixing Rails for Postgres Schemas furl:Fixing Rails for Postgres Schemas reddit:Fixing Rails for Postgres Schemas fark:Fixing Rails for Postgres Schemas blogmarks:Fixing Rails for Postgres Schemas Y!:Fixing Rails for Postgres Schemas smarking:Fixing Rails for Postgres Schemas magnolia:Fixing Rails for Postgres Schemas segnalo:Fixing Rails for Postgres Schemas

Fixing multipart POST in Rails

Posted by Guy Naor Tue, 30 May 2006 19:31:00 GMT

Our API is REST based, and so we need all the REST verbs to work. Unfortunately Rails doesn't work correctly with multipart PUT submits. The fix is very simple, and it's a one line change in the file actionpack/lib/action_controller/cgi_ext/raw_post_data_fix.rb:

--- actionpack/lib/action_controller/cgi_ext/raw_post_data_fix.rb       (revision 4380)
+++ actionpack/lib/action_controller/cgi_ext/raw_post_data_fix.rb       (working copy)
@@ -15,7 +15,7 @@
         method = :get
       end

-      if method == :post && (boundary = multipart_form_boundary)
+      if ((method == :post) || (method == :put)) && (boundary = multipart_form_boundary)
         @multipart = true
         @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
       else

The only change is adding the :put method to the if that makes the params into multiparams.

I'm posting this as a bug with the patch and the related tests to the Rail trac. Hopefuly it'll be included in a coming release of Rails.

Posted in  | 1 comment | no trackbacks

del.icio.us:Fixing multipart POST in Rails digg:Fixing multipart POST in Rails spurl:Fixing multipart POST in Rails wists:Fixing multipart POST in Rails simpy:Fixing multipart POST in Rails newsvine:Fixing multipart POST in Rails blinklist:Fixing multipart POST in Rails furl:Fixing multipart POST in Rails reddit:Fixing multipart POST in Rails fark:Fixing multipart POST in Rails blogmarks:Fixing multipart POST in Rails Y!:Fixing multipart POST in Rails smarking:Fixing multipart POST in Rails magnolia:Fixing multipart POST in Rails segnalo:Fixing multipart POST in Rails

Hash for views

Posted by Guy Naor Wed, 24 May 2006 21:40:00 GMT

One of my team members asked for a way to use non ActiveRecord objects in views. We settled on a modified hash that will have accessors for all the keys. Pretty simple with method_missing()

class ObjectiveHash < Hash

  def method_missing(method_id, *args, &block)
    begin
      super
    rescue NoMethodError => e
      is_assign = method_id.to_s[-1,1] == '='
      the_key = (method_id.to_s[0..(is_assign ? -2 : -1)]).to_sym
      self[the_key] = args if args && is_assign
      raise e if !self[the_key]  
      return self[the_key]
    end
  end

  private 
  def initialize other_hash = {}
    super
    update(other_hash)
  end

end

Now we can pass it to the view, and retireve into it on submit:

    
# Setting values
@settings = ObjectiveHash.new
@settings.name = "John"
@settings.family = "Doe"

# Getting values on submit
@settings = ObjectiveHash.new params[:settings].symbolize_keys

# Accessing the values
@settings.name # => John
@settings.family # => Doe

Please note that it'll be slower than regular hash access (which still works), and that all keys are symbols.

Posted in ,  | 5 comments | no trackbacks

del.icio.us:Hash for views digg:Hash for views spurl:Hash for views wists:Hash for views simpy:Hash for views newsvine:Hash for views blinklist:Hash for views furl:Hash for views reddit:Hash for views fark:Hash for views blogmarks:Hash for views Y!:Hash for views smarking:Hash for views magnolia:Hash for views segnalo:Hash for views

Older posts: 1 2 3 4 5

Subscribe to The Dev Blog