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?
Create a new rails application: rails .
Install the tzinfo plugin: script/plugin install tzinfo_timezone
Edit app/controllers/application.rb
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 Rails, Ruby | no comments
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 XMPP4R/Jabber, Rails, Ruby | no comments | no trackbacks
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 Rails | 4 comments | no trackbacks
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
if method_defined?(:formatter=)
log_str = ""
def format_message(severity, timestamp, progname, msg)
log_str = "[#{$$}][#{severity[0...1]}] msg\n"
end
else
def format_message(severity, timestamp, msg, progname)
log_str = "[#{$$}][#{severity[0...1]}] msg\n"
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
if method_defined?(:formatter=)
log_str = ""
def format_message(severity, timestamp, progname, msg)
log_str = "[#{$$}][#{severity[0...1]}] #{color_special_msg(msg, severity)}\n"
end
else
def format_message(severity, timestamp, msg, progname)
log_str = "[#{$$}][#{severity[0...1]}] #{color_special_msg(msg, severity)}\n"
end
end
def color_special_msg msg, severity
sub_color = severity[0...1] == 'E' ? '41' : '42'
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 Rails, Ruby | no comments | no trackbacks
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 Rails | no comments | no trackbacks
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 Rails, Ruby | no comments | no trackbacks
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
-
- result.last['.'] ? [result.first, result.last] : [result.first, "#{result[1]}.#{result[2]}"]
+
+
+ [result.first, result.last]
rescue
nil
end
Posted in Rails, Postgres | no comments | no trackbacks