<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1134218032421754894</id><updated>2011-04-21T10:57:11.521-07:00</updated><category term='ruby'/><category term='ajax world conference'/><category term='object parse'/><category term='selectors'/><category term='dom'/><category term='javascript'/><category term='php'/><category term='ajax'/><category term='screen scraping'/><category term='ssh'/><category term='applet'/><category term='jvm'/><category term='blog'/><category term='toJson'/><category term='www'/><category term='page height'/><category term='scrollHeight'/><category term='css precedence.'/><category term='clientHeight'/><category term='www::mechanize'/><category term='shell'/><category term='twitter'/><category term='environment variables'/><category term='rails'/><category term='doctype'/><category term='id'/><category term='json stringify'/><category term='mechanize'/><category term='w3c'/><category term='hype'/><title type='text'>Web Programming and Design</title><subtitle type='html'>Making a good web site and a good looking web site.  Also how to do it quickly.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://devers.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>10</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-1584543298921109055</id><published>2007-09-09T18:25:00.000-07:00</published><updated>2007-09-09T18:38:43.733-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='json stringify'/><category scheme='http://www.blogger.com/atom/ns#' term='object parse'/><category scheme='http://www.blogger.com/atom/ns#' term='toJson'/><title type='text'>Worlds Smallest toJson Function</title><content type='html'>While a google search of "toJSON javascript" revealed a few functions to convert javascript objects into strings, I was sadly dissappointed to see that most of them prototyped object its self (which you will recall every javascript object is prototyped from)  The general opinion of those javascript developers that I respect is that tampering with Object.prototype is &lt;a href="http://erik.eae.net/archives/2005/06/06/22.13.54"&gt;&lt;span style="text-decoration: underline;"&gt;verboten&lt;/span&gt;.&lt;/a&gt;  Once you mess with Object.prototype, you have to do the following in all of your for-in loops:&lt;br /&gt;&lt;br /&gt;for (var prop in obj) {&lt;br /&gt;   if (obj.hasOwnProperty(prop)) {&lt;br /&gt;       // Do Something&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Adding two lines isn't a huge deal, but again, seems like unnecessary overhead to me, especially since Object is rarely what I need to prototype, and prototyping Array and String still lets you use Object as a hash.&lt;br /&gt;&lt;br /&gt;So back to toJSON.  I decided since I couldn't find a json parser that didn't prototype Object, I would write my own.  This one is pretty basic, but here goes:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;function toJson(obj) {&lt;br /&gt; switch (typeof obj) {&lt;br /&gt;  case 'object':&lt;br /&gt;   if (obj) {&lt;br /&gt;    var list = [];&lt;br /&gt;    if (obj instanceof Array) {&lt;br /&gt;     for (var i=0;i &lt; obj.length;i++) {&lt;br /&gt;      list.push(toJson(obj[i]));&lt;br /&gt;     }&lt;br /&gt;     return '[' + list.join(',') + ']';&lt;br /&gt;    } else {&lt;br /&gt;     for (var prop in obj) {&lt;br /&gt;      list.push('"' + prop + '":' + toJson(obj[prop]));&lt;br /&gt;     }&lt;br /&gt;     return '{' + list.join(',') + '}';&lt;br /&gt;    }&lt;br /&gt;   } else {&lt;br /&gt;    return 'null';&lt;br /&gt;   }&lt;br /&gt;  case 'string':&lt;br /&gt;   return '"' + obj.replace(/(["'])/g, '\\$1') + '"';&lt;br /&gt;  case 'number':&lt;br /&gt;  case 'boolean':&lt;br /&gt;   return new String(obj);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;At least its short, all of the other parsers were much larger.  Also note that my escaping and such is pretty basic, and probably needs some improvement, but this served my purpose, and again was small.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-1584543298921109055?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/1584543298921109055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=1584543298921109055' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/1584543298921109055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/1584543298921109055'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2007/09/worlds-smallest-tojson-function.html' title='Worlds Smallest toJson Function'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-7837346502206940147</id><published>2007-05-02T19:27:00.000-07:00</published><updated>2007-05-02T19:29:26.158-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hype'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><title type='text'>Something I can't understand...</title><content type='html'>Why is &lt;a href="http://alexa.com/data/details/traffic_details?url=www.twitter.com"&gt;this&lt;/a&gt; happening?  How is it possible that twitter is getting popular?  Perhaps there is more to this site than I am seeing, but from the looks of it, it is just a site where you update your status message, yet everyone wants to be &amp;quot;the next twitter&amp;quot;.  I guess this just reassures my theory that all that really matters is if you can get hype.  My new plan is to build something really simple and then promote it like none other.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-7837346502206940147?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/7837346502206940147/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=7837346502206940147' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/7837346502206940147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/7837346502206940147'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2007/05/something-i-cant-understand.html' title='Something I can&apos;t understand...'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-4514672848623814919</id><published>2007-05-02T12:05:00.000-07:00</published><updated>2007-05-02T12:10:55.725-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='environment variables'/><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Getting a real shell</title><content type='html'>At my work I have been making a bunch of textmate commands to automate some of the build process, and specifically launching the currently open file.  To do this, I run a command kind of like this:&lt;br /&gt;&lt;br /&gt;ssh root@.... "copytobeta $TM_FILENAME"&lt;br /&gt;&lt;br /&gt;(note: this is a bit simplified, and I have my computer setup with shared keys, so I don't have to login :-)&lt;br /&gt;&lt;br /&gt;cpBeta is a command we have on our servers.  However, when I try this I get that the command cpBeta can not be found.  Clearly the problem here is that the environment variables are not getting passed correctly, so next I tried running everything in the sh command&lt;br /&gt;&lt;br /&gt;ssh root@.... "sh -c \"copytobeta $TM_FILENAME\""&lt;br /&gt;&lt;br /&gt;still no luck.  I tried all kinds of things, first running /root/.bash_profile, then /root/.bashrc...  nothin..&lt;br /&gt;&lt;br /&gt;So finally one of my co-workers pointed me to the --login option of sh (or bash)  The solution looks something like this.&lt;br /&gt;&lt;br /&gt;ssh root@.... "sh --login -c \"copytobeta $TM_FILENAME\""&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-4514672848623814919?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/4514672848623814919/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=4514672848623814919' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4514672848623814919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4514672848623814919'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2007/05/getting-real-shell.html' title='Getting a real shell'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-4816980217058989208</id><published>2007-01-19T16:06:00.000-08:00</published><updated>2007-01-19T16:20:55.527-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='w3c'/><category scheme='http://www.blogger.com/atom/ns#' term='id'/><category scheme='http://www.blogger.com/atom/ns#' term='selectors'/><category scheme='http://www.blogger.com/atom/ns#' term='css precedence.'/><title type='text'>Specificity (And why css does what it does)</title><content type='html'>So my friends and I at my work (http://www.freewebs.com) were trying to figure out the browsers algorithm for &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_0"&gt;determining&lt;/span&gt; which &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;css&lt;/span&gt; rules take &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_2"&gt;precedence&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Here's an example.  The following produces a blue h2&lt;br /&gt;&lt;br /&gt;&amp;lt;style&amp;gt;&lt;br /&gt;div h2 {&lt;br /&gt; color: red;&lt;br /&gt;}&lt;br /&gt;#myH2 {&lt;br /&gt; color: blue;&lt;br /&gt;}&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div id="myDiv"&amp;gt;&lt;br /&gt;&amp;lt;h2 id="myH2"&amp;gt;My Heading&amp;lt;/h2&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;So we concluded that obviously id's take precedence over tags.&lt;br /&gt;&lt;br /&gt;However, in this situation we end up with a red h2&lt;br /&gt;&lt;br /&gt;&amp;lt;style&amp;gt;&lt;br /&gt;#myDiv h2 {&lt;br /&gt; color: red;&lt;br /&gt;}&lt;br /&gt;#myH2 {&lt;br /&gt; color: blue;&lt;br /&gt;}&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div id="myDiv"&amp;gt;&lt;br /&gt;&amp;lt;h2 id="myH2"&amp;gt;My Heading&amp;lt;/h2&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;It turns out after a bit of searching that the algorithm is pretty simple.  Here's what the &lt;a href="http://www.w3.org/TR/REC-CSS2/cascade.html#specificity"&gt;w3c had to say&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;A selector's specificity is calculated as follows:&lt;/p&gt;  &lt;ul&gt;&lt;li&gt;count the number of ID attributes in the selector (= a) &lt;/li&gt;&lt;li&gt;count the number of other attributes and pseudo-classes  in the selector (= b) &lt;/li&gt;&lt;li&gt;count the number of element names in the selector (= c) &lt;/li&gt;&lt;li&gt;ignore pseudo-elements. &lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.   &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-4816980217058989208?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/4816980217058989208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=4816980217058989208' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4816980217058989208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4816980217058989208'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2007/01/specificity-and-why-css-does-what-it.html' title='Specificity (And why css does what it does)'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-6241557008791104784</id><published>2007-01-18T17:28:00.000-08:00</published><updated>2007-01-18T17:31:46.145-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='dom'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax world conference'/><title type='text'>I'm speaking at Ajax World Conference in NY</title><content type='html'>So, if anyone is in new york in march and board, I'll be presenting on "Workarounds ever javascript developer should know" at Ajax World Conference.  It should hopefully be a good presentation.  At the moment I have been having fun playing with keynote for mac and trying to find make some simple sample code.  With any luck, it should help people figure out how to get a better end user experience and maybe make a few of the problems they run into a little easier to solve.  I think I am also going to focus on &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_0"&gt;performance&lt;/span&gt; &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;a lot&lt;/span&gt;, but who knows how it will end up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-6241557008791104784?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/6241557008791104784/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=6241557008791104784' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/6241557008791104784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/6241557008791104784'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2007/01/im-speaking-at-ajax-world-conference-in.html' title='I&apos;m speaking at Ajax World Conference in NY'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-4332729890552181106</id><published>2006-12-13T07:52:00.000-08:00</published><updated>2006-12-13T07:53:01.238-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='applet'/><category scheme='http://www.blogger.com/atom/ns#' term='jvm'/><title type='text'>So cool</title><content type='html'>While the JVM is a beast when it comes to load time and applets are on the verge of death, &lt;a href="http://headius.blogspot.com/2006/11/ruby-for-web-check.html"&gt;this is cool&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-4332729890552181106?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/4332729890552181106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=4332729890552181106' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4332729890552181106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4332729890552181106'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2006/12/so-cool.html' title='So cool'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-4830683307943422474</id><published>2006-11-17T11:52:00.000-08:00</published><updated>2006-11-17T12:33:17.286-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scrollHeight'/><category scheme='http://www.blogger.com/atom/ns#' term='doctype'/><category scheme='http://www.blogger.com/atom/ns#' term='page height'/><category scheme='http://www.blogger.com/atom/ns#' term='clientHeight'/><title type='text'>Height of the Page, no questions asked</title><content type='html'>When creating a popover for a &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_0"&gt;web app&lt;/span&gt; I was writing, I needed to get the internal height of the page, so I could set the popover to the pages height.  There are other techniques to &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_1"&gt;achieve&lt;/span&gt; this such as setting&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt; html {&lt;br /&gt;     overflow: hidden;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; body {&lt;br /&gt;     overflow: auto;&lt;br /&gt; }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt; then absolutely positioning the div, but this can result in rendering bugs in IE anytime you try to relatively position an element.  (usually the element doesn't move when you scroll the page)&lt;br /&gt; So the simple solution is to use javascript to get the pages height when the popover is supposed to be show.  For the popover I was working on, I did not want to change the underlying page's &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;doctype&lt;/span&gt; (or lack there of)  The problem was that document.documentElement.clientHeight only works with a &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;doctype&lt;/span&gt;.  To make a long story short, I came up with this line of code that should return the pages complete height no matter what.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Math.max((window.innerHeight || 0), document.body.clientHeight, document.documentElement.clientHeight, document.body.scrollHeight)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt; Simple, parts might be &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_4"&gt;repetitive&lt;/span&gt;, but it works&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-4830683307943422474?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/4830683307943422474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=4830683307943422474' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4830683307943422474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4830683307943422474'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2006/11/height-of-page-no-questions-asked.html' title='Height of the Page, no questions asked'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-787536074733531664</id><published>2006-11-12T15:34:00.000-08:00</published><updated>2006-11-12T15:45:02.880-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='www'/><category scheme='http://www.blogger.com/atom/ns#' term='screen scraping'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='www::mechanize'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='mechanize'/><title type='text'>WWW::Mechanize get current page</title><content type='html'>So I have always done a lot of screen scraping, and typically whatever language I was working with I would build a framework to get the job done.  I built one for java, which was a nightmare.  Next I created one in php.  It was a lot simpler, but just took to much time to really do right.  When I moved to ruby I was supprised to find the &lt;a href="http://www.ntecs.de/blog-old/Blog/WWW-Mechanize.rdoc"&gt;WWW::Mechanize&lt;/a&gt; &lt;a href="http://rubyforge.org/projects/wee"&gt;library&lt;/a&gt;.  It did everything I had been building into these other frameworks.  The nice thing about mechanize is that it takes care of following redirects, and parsing the html into an easy to follow structure.  Something I would always build into my frameworks was the ability to psuedo-submit forms on the page.  Typically in the form of (php example):&lt;br /&gt;&lt;br /&gt;$cForm = $page=&gt;forms[2]&lt;br /&gt;$cForm=&gt;login = 'bob';&lt;br /&gt;$cForm=&gt;password = '&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;testpass&lt;/span&gt;';&lt;br /&gt;$&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;cForm&lt;/span&gt;.submit();&lt;br /&gt;&lt;br /&gt;You can do very &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;similiar&lt;/span&gt; things in mechanize, but the thing that stumped me for to long was how you got the current &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;url&lt;/span&gt; of the page.  Turns out it isn't that hard, but it is poorly documented.&lt;br /&gt;&lt;br /&gt;(ruby example)&lt;br /&gt;&lt;br /&gt;agent = WWW::Mechanize.new&lt;br /&gt;agent.user_agent = 'Mozilla/4.0 (compatible; &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;MSIE&lt;/span&gt; 7.0b; Windows NT 5.1)'&lt;br /&gt;form = browse.forms[1]&lt;br /&gt;form.fields.find {|f| f.name == 'location'}.value = 'MT'&lt;br /&gt;page1 = @agent.submit(form, form.buttons.first)&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;agent.page.uri.to_s&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Note the last line.  This should always return the page that the agent is "at" in the browser &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_5"&gt;paradigm&lt;/span&gt;, even after multiple redirects&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-787536074733531664?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/787536074733531664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=787536074733531664' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/787536074733531664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/787536074733531664'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2006/11/wwwmechanize-get-current-page.html' title='WWW::Mechanize get current page'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-7092505512054439385</id><published>2006-10-22T11:08:00.000-07:00</published><updated>2006-10-23T18:03:33.300-07:00</updated><title type='text'>acts_as_ferret - Sort by something other than rank</title><content type='html'>&lt;style&gt;&lt;br /&gt;/* Stylesheet generated from TextMate theme&lt;br /&gt; *&lt;br /&gt; * Mac Classic&lt;br /&gt; * &lt;br /&gt; *&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;pre.textmate-source {&lt;br /&gt; margin: 0;&lt;br /&gt; padding: 0 0 0 2px;&lt;br /&gt; font-family: Monaco, monospace;&lt;br /&gt; font-size: 11px;&lt;br /&gt; line-height: 1.3em;&lt;br /&gt; word-wrap: break-word;&lt;br /&gt; white-space: pre;&lt;br /&gt; white-space: pre-wrap;&lt;br /&gt; white-space: -moz-pre-wrap;&lt;br /&gt; white-space: -o-pre-wrap;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;pre.textmate-source.mac_classic {&lt;br /&gt; color: #000000;&lt;br /&gt; background-color: #FFFFFF;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;pre.textmate-source .linenum {&lt;br /&gt; width: 75px;&lt;br /&gt; padding: 0.1em 1em 0.2em 0;&lt;br /&gt; color: #888;&lt;br /&gt; background-color: #eee;&lt;br /&gt;}&lt;br /&gt;pre.textmate-source.mac_classic span {&lt;br /&gt;   padding-top: 0.2em;&lt;br /&gt;   padding-bottom: 0.1em;&lt;br /&gt;}&lt;br /&gt;pre.textmate-source.mac_classic ::selection {&lt;br /&gt; background-color: #C3DCFF;&lt;br /&gt;}&lt;br /&gt;/* Comment */&lt;br /&gt;pre.textmate-source.mac_classic .comment {&lt;br /&gt; color: #0066FF;&lt;br /&gt; font-style: italic;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Keyword */&lt;br /&gt;pre.textmate-source.mac_classic .keyword, pre.textmate-source.mac_classic .storage {&lt;br /&gt; color: #0000FF;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Number */&lt;br /&gt;pre.textmate-source.mac_classic .constant_numeric {&lt;br /&gt; color: #0000CD;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* User-defined constant */&lt;br /&gt;pre.textmate-source.mac_classic .constant {&lt;br /&gt; color: #C5060B;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Built-in constant */&lt;br /&gt;pre.textmate-source.mac_classic .constant_language {&lt;br /&gt; color: #585CF6;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Variable */&lt;br /&gt;pre.textmate-source.mac_classic .variable_language, pre.textmate-source.mac_classic .variable_other {&lt;br /&gt; color: #318495;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* String */&lt;br /&gt;pre.textmate-source.mac_classic .string {&lt;br /&gt; color: #036A07;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* String interpolation */&lt;br /&gt;pre.textmate-source.mac_classic .constant_character_escape, pre.textmate-source.mac_classic .string .source {&lt;br /&gt; color: #26B31A;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Preprocessor line */&lt;br /&gt;pre.textmate-source.mac_classic .meta_preprocessor {&lt;br /&gt; color: #1A921C;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Preprocessor directive */&lt;br /&gt;pre.textmate-source.mac_classic .keyword_control_import {&lt;br /&gt; color: #0C450D;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Function name */&lt;br /&gt;pre.textmate-source.mac_classic .entity_name_function, pre.textmate-source.mac_classic .keyword_other_name-of-parameter_objc {&lt;br /&gt; color: #0000A2;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Type name */&lt;br /&gt;pre.textmate-source.mac_classic .entity_name_type {&lt;br /&gt; text-decoration: underline;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Inherited class name */&lt;br /&gt;pre.textmate-source.mac_classic .entity_other_inherited-class {&lt;br /&gt; font-style: italic;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Function parameter */&lt;br /&gt;pre.textmate-source.mac_classic .variable_parameter {&lt;br /&gt; font-style: italic;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Function argument and result types */&lt;br /&gt;pre.textmate-source.mac_classic .storage_type_method {&lt;br /&gt; color: #70727E;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Section */&lt;br /&gt;pre.textmate-source.mac_classic .meta_section .entity_name_section, pre.textmate-source.mac_classic .declaration_section .entity_name_section {&lt;br /&gt; font-style: italic;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Library function */&lt;br /&gt;pre.textmate-source.mac_classic .support_function {&lt;br /&gt; color: #3C4C72;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Library object */&lt;br /&gt;pre.textmate-source.mac_classic .support_class, pre.textmate-source.mac_classic .support_type {&lt;br /&gt; color: #6D79DE;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Library constant */&lt;br /&gt;pre.textmate-source.mac_classic .support_constant {&lt;br /&gt; color: #06960E;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Library variable */&lt;br /&gt;pre.textmate-source.mac_classic .support_variable {&lt;br /&gt; color: #21439C;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* JS: Operator */&lt;br /&gt;pre.textmate-source.mac_classic .keyword_operator_js {&lt;br /&gt; color: #687687;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Invalid */&lt;br /&gt;pre.textmate-source.mac_classic .invalid {&lt;br /&gt; color: #FFFFFF;&lt;br /&gt; background-color: #990000;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Invalid trailing whitespace */&lt;br /&gt;pre.textmate-source.mac_classic .invalid_deprecated_trailing-whitespace {&lt;br /&gt; background-color: #FFD0D0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Embedded source */&lt;br /&gt;pre.textmate-source.mac_classic .text .source, pre.textmate-source.mac_classic .string_unquoted {&lt;br /&gt; background-color: rgba(66, 127, 245, 0.19);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup XML declaration */&lt;br /&gt;pre.textmate-source.mac_classic .meta_tag_preprocessor_xml {&lt;br /&gt; color: #68685B;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup DOCTYPE */&lt;br /&gt;pre.textmate-source.mac_classic .meta_tag_sgml_doctype, pre.textmate-source.mac_classic .meta_tag_sgml_doctype .entity, pre.textmate-source.mac_classic .meta_tag_sgml_doctype .string, pre.textmate-source.mac_classic .meta_tag_preprocessor_xml, pre.textmate-source.mac_classic .meta_tag_preprocessor_xml .entity, pre.textmate-source.mac_classic .meta_tag_preprocessor_xml .string {&lt;br /&gt; color: #888888;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup DTD */&lt;br /&gt;pre.textmate-source.mac_classic .string_quoted_docinfo_doctype_DTD {&lt;br /&gt; font-style: italic;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup tag */&lt;br /&gt;pre.textmate-source.mac_classic .meta_tag, pre.textmate-source.mac_classic .declaration_tag {&lt;br /&gt; color: #1C02FF;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup name of tag */&lt;br /&gt;pre.textmate-source.mac_classic .entity_name_tag {&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup tag attribute */&lt;br /&gt;pre.textmate-source.mac_classic .entity_other_attribute-name {&lt;br /&gt; font-style: italic;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup: Heading */&lt;br /&gt;pre.textmate-source.mac_classic .markup_heading {&lt;br /&gt; color: #0C07FF;&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup: Quote */&lt;br /&gt;pre.textmate-source.mac_classic .markup_quote {&lt;br /&gt; color: #000000;&lt;br /&gt; font-style: italic;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Markup: List */&lt;br /&gt;pre.textmate-source.mac_classic .markup_list {&lt;br /&gt; color: #B90690;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/style&gt;&lt;span style="font-size:130%;"&gt;The Old&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;While rebuilding &lt;a href="http://www.audiri.com/"&gt;audiri.com&lt;/a&gt;  I decided that I need to revamp the old search system.  The site currently lets you search for artists by name (useless, since you probably don't know the name of the band that you are looking for) and also by artists who sound similar to an artist you already know.  So if you entered in &lt;span style="font-style: italic;"&gt;rage against the machine&lt;/span&gt; it would return all of the bands who had put &lt;span style="font-style: italic;"&gt;rage against the machine&lt;/span&gt; into their "similar artists" field.  Previously the search was as simple as that.  I used a mysql full-text index and matched the query to the fields.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;The New&lt;br /&gt;&lt;/span&gt;I decided acts_as_ferret was an easy way to get a powerful full text index.  To make a model searchable, simple specify:&lt;br /&gt;&lt;pre&gt;acts_as_ferret&lt;br /&gt;&lt;/pre&gt;in the model.&lt;br /&gt;&lt;br /&gt;You can also specify which fields as follows:&lt;br /&gt;&lt;pre&gt;acts_as_ferret :fields =&gt; {&lt;br /&gt; 'artist_name' =&gt; {:boost =&gt; 5},&lt;br /&gt; 'similar_artists' =&gt; {:boost =&gt; 2},&lt;br /&gt; 'influences' =&gt; {},&lt;br /&gt; 'description' =&gt; {},&lt;br /&gt; 'short_description' =&gt; {},&lt;br /&gt; 'style' =&gt; {:boost =&gt; 0.5},&lt;br /&gt; 'history' =&gt; {:boost =&gt; 0.5},&lt;br /&gt; 'genre_tags' =&gt; {},&lt;br /&gt; :comments =&gt; {:boost =&gt; 3},&lt;br /&gt; 'total_plays' =&gt; {:index =&gt; :yes, :store =&gt; :no},&lt;br /&gt; 'total_plays_today' =&gt; {:index =&gt; :yes, :store =&gt; :no},&lt;br /&gt; 'plays_this_day' =&gt; {:index =&gt; :yes, :store =&gt; :no},&lt;br /&gt; 'plays_this_week' =&gt; {:index =&gt; :yes, :store =&gt; :no},&lt;br /&gt; 'plays_this_month' =&gt; {:index =&gt; :yes, :store =&gt; :no},&lt;br /&gt; 'plays_ever' =&gt; {:index =&gt; :yes, :store =&gt; :no},&lt;br /&gt; 'hidden' =&gt; {:index =&gt; :yes, :store =&gt; :no}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One thing that I thought would be useful would be searching all of the comments people left on the artists page.  It turns out this wasn't to hard, to do a has_many relationship with acts_as_ferret, you bascially just include a function that returns a dump of the text for each artist.&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&gt;def comments&lt;br /&gt; artist_comments.map {|ac| ac.body}.join(' ')&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;Then in the fields area say:&lt;code&gt;&lt;br /&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&gt;:comments =&gt; {},&lt;br /&gt;&lt;/pre&gt;The fact that it's a symbol tells acts_as_ferret that it's a function.&lt;br /&gt;&lt;br /&gt;This all worked out great.  I could quickly query acts_as_ferret using:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&gt;Artist.find_by_contents("query")&lt;br /&gt;&lt;/pre&gt;and get back some pretty good results.&lt;br /&gt;&lt;br /&gt;So the next question I had was how do I sort the results by something other than just relevence.  Normally on audiri we give people the option to sort by things like total_plays and the like.  To do this really required pulling away at the acts_as_ferret model and delving under it to ferret it's self.  Fortunately for us, any model made with acts_as_ferret gives us the .ferret_index structure.  This lets us make method calls on the actual ferret index.  Another issue I was trying to tackle was pagination natively in ferret.  A lot of solutions suggest pulling out all of the ferret results, then sorting and paging with sql or by hand.  This was not my idea solution.&lt;br /&gt;&lt;br /&gt;Borrowing code from &lt;a href="http://blog.zmok.net/articles/category/how-to"&gt;various sources&lt;/a&gt; , I was able to make a function in the model which let me query the index how I wanted.&lt;br /&gt;&lt;pre class="textmate-source"&gt;&lt;span class="linenum"&gt;    1&lt;/span&gt; &lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_rails meta_rails_model"&gt;&lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;  &lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;self.full_text_search&lt;/span&gt;(&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;q, options = {}&lt;/span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;    2&lt;/span&gt;     &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;return&lt;/span&gt; &lt;span class="constant constant_language constant_language_ruby"&gt;nil&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;if&lt;/span&gt; q.nil? &lt;span class="keyword keyword_operator keyword_operator_logical keyword_operator_logical_ruby"&gt;or&lt;/span&gt; q==&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;""&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;    3&lt;/span&gt;     default_options = {&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:limit&lt;/span&gt; =&amp;gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;10&lt;/span&gt;, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:page&lt;/span&gt; =&amp;gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;1&lt;/span&gt;, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:sort&lt;/span&gt; =&amp;gt; &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;''&lt;/span&gt;}&lt;br /&gt;&lt;span class="linenum"&gt;    4&lt;/span&gt;     options = default_options.merge options&lt;br /&gt;&lt;span class="linenum"&gt;    5&lt;/span&gt;     options[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:offset&lt;/span&gt;] = options[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:limit&lt;/span&gt;] * (options[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:page&lt;/span&gt;].to_i-&lt;span class="constant constant_numeric constant_numeric_ruby"&gt;1&lt;/span&gt;)&lt;br /&gt;&lt;span class="linenum"&gt;    6&lt;/span&gt;     results = []&lt;br /&gt;&lt;span class="linenum"&gt;    7&lt;/span&gt;   &lt;br /&gt;&lt;span class="linenum"&gt;    8&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;    9&lt;/span&gt;     &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;   10&lt;/span&gt;   &lt;br /&gt;&lt;span class="linenum"&gt;   11&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;   12&lt;/span&gt;     &lt;span class="keyword keyword_control keyword_control_ruby"&gt;if&lt;/span&gt; q == &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;''&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;   13&lt;/span&gt;       search_map = &lt;span class="variable variable_language variable_language_ruby"&gt;self&lt;/span&gt;.ferret_index.search(&lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;'*:('&lt;/span&gt;+q+&lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;') '&lt;/span&gt;, {&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:limit&lt;/span&gt; =&amp;gt; options[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:limit&lt;/span&gt;], &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:offset&lt;/span&gt; =&amp;gt; options[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:offset&lt;/span&gt;]})&lt;br /&gt;&lt;span class="linenum"&gt;   14&lt;/span&gt;     &lt;span class="keyword keyword_control keyword_control_ruby"&gt;else&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;   15&lt;/span&gt;       search_map = &lt;span class="variable variable_language variable_language_ruby"&gt;self&lt;/span&gt;.ferret_index.search(&lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;'*:('&lt;/span&gt;+q+&lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;') '&lt;/span&gt;, {&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:limit&lt;/span&gt; =&amp;gt; options[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:limit&lt;/span&gt;], &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:offset&lt;/span&gt; =&amp;gt; options[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:offset&lt;/span&gt;], &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:sort&lt;/span&gt; =&amp;gt; options[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;:sort&lt;/span&gt;]})&lt;br /&gt;&lt;span class="linenum"&gt;   16&lt;/span&gt;     &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;   17&lt;/span&gt;&lt;br /&gt;&lt;span class="linenum"&gt;   18&lt;/span&gt; &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;    # loop through each&lt;br /&gt;&lt;/span&gt;&lt;span class="linenum"&gt;   19&lt;/span&gt;     search_map.hits.each {&lt;span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"&gt; &lt;/span&gt;|t|&lt;br /&gt;&lt;span class="linenum"&gt;   20&lt;/span&gt;       results &amp;lt;&amp;lt; &lt;span class="variable variable_language variable_language_ruby"&gt;self&lt;/span&gt;.find(&lt;span class="variable variable_language variable_language_ruby"&gt;self&lt;/span&gt;.ferret_index[t.doc][&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;"id"&lt;/span&gt;])&lt;br /&gt;&lt;span class="linenum"&gt;   21&lt;/span&gt;     }&lt;br /&gt;&lt;span class="linenum"&gt;   22&lt;/span&gt;       &lt;br /&gt;&lt;span class="linenum"&gt;   23&lt;/span&gt;     num = search_map.total_hits&lt;br /&gt;&lt;span class="linenum"&gt;   24&lt;/span&gt;     &lt;br /&gt;&lt;span class="linenum"&gt;   25&lt;/span&gt;     &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;return&lt;/span&gt; [num, results]&lt;br /&gt;&lt;span class="linenum"&gt;   26&lt;/span&gt;   &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;The getOrder is a helper funciton that returns things like:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&gt;total_plays DESC&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that in lucene's query language, ASC is not used, only DESC.&lt;br /&gt;&lt;br /&gt;Hope this helps people get started&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-7092505512054439385?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/7092505512054439385/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=7092505512054439385' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/7092505512054439385'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/7092505512054439385'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2006/10/actsasferret-sort-by-something-other.html' title='acts_as_ferret - Sort by something other than rank'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1134218032421754894.post-4728815225120946313</id><published>2006-09-08T11:21:00.000-07:00</published><updated>2006-09-08T11:28:05.152-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><title type='text'>How can you be a developer and not have a blog</title><content type='html'>The question of the day I guess.  Truth be told, I don't have a lot of things that a developer should have.  I just recently got a cell phone.  So why if I hold out on all of these other things should I start a blog.  Let me clear the air, I have no illusions of grandure.  I am well aware that the average blog has .5 readers.  (Including the authors)  I however feel that this servers as a good place to give people an idea of what I do an maybe save people a little bit of code or searching in cases.  So without further ado, here's my first blog post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1134218032421754894-4728815225120946313?l=devers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://devers.blogspot.com/feeds/4728815225120946313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1134218032421754894&amp;postID=4728815225120946313' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4728815225120946313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1134218032421754894/posts/default/4728815225120946313'/><link rel='alternate' type='text/html' href='http://devers.blogspot.com/2006/09/how-can-you-be-developer-and-not-have.html' title='How can you be a developer and not have a blog'/><author><name>Ryan Stout</name><uri>http://www.blogger.com/profile/16024355224253838106</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://www.freewebs.com/ryanstout/freewebspic.jpg'/></author><thr:total>0</thr:total></entry></feed>
