Posted by Guy Naor
Sat, 14 Oct 2006 15:34:00 GMT
Time to get down to some sample code!
In this part I will show how to log in to a Jabber server and send a simple message. This is the basis of everything else, and will give you something nice to start with. After all, sending messages is the most common use for Jabber.
Installation
Get the code from here, unpack it and then run ./setup.rb from the directory you open the archive to. YOu can also get the gem and install it locally. As it's pure ruby, there aren't any complications with it.
What we will need?
To start, get irb going as we'll do it interactively so we can get immediate results. You will also need a Jabber account you can login to, and another account to send messages to. You could send to the same account, actually, but it's nicer with a second one.
For this session, I will be using my account at DreamHost - they provide Jabber as one of the features of the account, which is pretty cool. YOu can also setup one of your own, but it's beyons the scope of this post.
Server: yeush.com
User: test
Password: test (don't worry, I'm not this crazy, this is a fake password...)
And we'll interact with another accounts (that has the test account as a buddy that can connect to): test_with_me@yeush.com.
Now in irb, make sure we have xmpp4r included:
irb
require 'xmpp4r/client'
include Jabber
Logging in
To start any communication with a Jabber server, we need to first log in to the server:
jid = JID::new('test@yeush.com/Testing')
password = 'test'
cl = Client::new(jid)
cl.connect
cl.auth(password)
That's all it takes to login, really! Now for some explanations. The jabber system is made of a collection of servers that can be either stand alone, or connected to each other (called open federation). Each entity connecting to the network must have a unique ID, called a JID (Jabber ID). The id is made up of the user name, server the user is on and an optional resource. The resource is the part after the /, in this case /Testing. The resources can be arbitrarily named, and allow the same person/entity to be connected and talking from many places at once, while still knowing where to send replies back to.
Now that we are connected, let's do something with it...
Sending a simple message
Most of what we want to do in a Jabber session is send messages. So let's get one going:
to = "test_with_me@yeush.com"
subject = "XMPP4R test"
body = "Hi, this is my first try from XMPP4R!!!"
m = Message::new(to, body).set_type(:normal).set_id('1').set_subject(subject)
cl.send m
What did we have here? We assigned a few parameters, created a new message object, and had the client send it. subject is mostly ignored by clients. Very few will show it. body is the plain text message you want to send. *to* is well, the to.
A message is simple an REXML document, and the calls to setid('1') and settype(:normal), set those values in the message element. :normal is the message type. Look in the documentation for all possible types. We will be using :normal and :chat. id is used to identify the message, and can be anything. Usually it's an advancing counter, and it can also include letters and underscore.
After we have the message constructed, we let the client send it. Like I explained in the first part, the whole XMPP protocol is based on XML documents being sent and received. You can type m.to_s in irb to get the document representation. Here is how the message looks in XML:
<message type='normal' id='1' to='test_with_me@yeush.com'>
<body>Hi, this is my first try from XMPP4R!!!</body>
<subject>XMPP4R test</subject>
</message>
Go and play with that a bit, and on the next installment we'll see how to add rich-text to the messages (hint: adding it to the body won't work - you need a special html element for that).
See you next time...
Posted in Ruby, XMPP4R/Jabber | no comments | no trackbacks
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 Rails, Ruby, XMPP4R/Jabber | 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
Wed, 14 Jun 2006 14:20:00 GMT
Giving our Famundo users the ability to change some background colors on the header to match the logo, brough up the problem of selecting the correct color to use for text we have to write on the header. We can't default to just a specific color as it might not be readable on some of the colors.
The W3C recommends how to select colors to achieve good readability. It's a nice formula to find the color contrast and brightness difference. They also recommend on a treshold to compare against, when selecting the colors. I wrote ruby code to do that calculation. You can change the tresholds to give you more latitute in selecting colors.
def colors_diff_ok? c1, c2
cont, bright = find_color_diff c1, c2
(cont > 500) && (bright > 125)
end
def find_color_diff c1, c2
r1, g1, b1 = break_color c1
r2, g2, b2 = break_color c2
cont_diff = (r1-r2).abs+(g1-g2).abs+(b1-b2).abs
bright1 = (r1 * 299 + g1 * 587 + b1 * 114) / 1000
bright2 = (r2 * 299 + g2 * 587 + b2 * 114) / 1000
brt_diff = (bright1 - bright2).abs
[cont_diff, brt_diff]
end
def break_color rgb
r = (rgb & 0xff0000) >> 16
g = (rgb & 0x00ff00) >> 8
b = rgb & 0x0000ff
[r,g,b]
end
A simple way to use it, is to have an array with different color options, and look in it for a match:
possible_colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0x00ffff, 0xffffff]
good_color = 0
possible_colors.each do |c|
if colors_diff_ok? c, my_color
good_color = c
break
end
end
Posted in 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