Garbage Burrito - Latest Blog Entries http://garbageburrito.com/blog en-us Kitchen Renovation - Round Two <p>When we first bought our house, the kitchen was unusable. For quite a while we had a fridge full of Hot Pockets and a microwave. I'll never eat another hot pocket again.</p><p>The first renovation was a get something in cheap and quick. It worked, but just didn't have that finished feel to it. So last winter we bought some IKEA cabinets on eBay, and this is the fruit of our labor.</p><p class="center_image"><a href="/album/album/8933"><img alt="Kitchen" height="338" src="/media/AA/AA/ben/images/145578/main/IMG_0048.jpg" width="450" />&#160;</a></p><p class="center_image"><a href="/album/album/8933">Click for more pics</a></p><p>Still some work to do, but for the most part we're very happy with the results.</p> Thu, 12 Jun 2008 15:39:00 -0500 http://garbageburrito.com/blog/entry/29424/kitchen-renovation-round-two http://garbageburrito.com/blog/entry/29424/kitchen-renovation-round-two New Gig at Secure Passage <p>So I was at Sprint for 6 months, I tried to post something about it here, but was asked by my manager the next day to take it down. Creepy, eh? Anyway, my contract was up for extension, and it went down something like this.</p><p>I was told I'd be extended, cool. We were told all the projects were cancelled, great. We were told no contractors would be extended, super. I found another job, nice. Just a couple days before my last day I was told the project where re-instated, and asked if I wanted to stay, totally awesome.</p><p>I might have considered staying, but I just happened to find one of the coolest places to work in KC. Secure Passage writes Firewall Monitoring software. They've got a mix of technologies, but it seems like the main direction is to move towards Java.</p><p>Anyway, it's a small shop, about 7 developers. It's very much a get things done type of atomosphere, which I miss from the good ole Bensoft days. And best of all I get to dress casual all week. Woohoo!</p><p>I've been here for about a month. So far I'm having lots of fun.</p> Thu, 12 Jun 2008 06:38:00 -0500 http://garbageburrito.com/blog/entry/29376/new-gig-at-secure-passage http://garbageburrito.com/blog/entry/29376/new-gig-at-secure-passage Lessons learned from the iPhone <p><img alt="iPhone" class="right" height="227" src="/media/image/image/37150/main/iphone.jpg" width="200" />So I bought an iPhone a month ago, and needless to say I'm very impressed by it. I knew since that day when I was spastically refreshing engadget as they posted Job's keynote, that this device was a game changer. Though my natural disdain for Apple's pretentious commercials swayed my opinion back to skeptic. &quot;Yeah, I can check my email and surf the web on my Windows Mobile phone too, so WHAT?!&quot; But I couldn't resist for long, and as usual I bought myself the thing that my wife was planning on buying me for Christmas.</p><p>I have to say that this is a revolutionary device. I know I'm late to the party on iPhone praise, but there are a couple interesting things I realized while I tried to find excuses to use it every hour of every day.</p><p>The first is a sort of reassurance I get through the similarities I see to Doodlekit. The design philosophies are very much the same, and it's good to see that such an opinionated device can be so widely accepted.</p><p>More importantly however, it's finally allow me to quantify why simple is better. Before I could sit down and explain examples of why simplicity is key, but it was never so clear cut.</p><p><strong>Distraction</strong></p><p>My old Windows Mobile device had a lot of features and applications, more than the iPhone. But here's the thing, I never used them. However I find myself using every single application on my iPhone. The problem with the WinMo phone is that I was distracted by choices. For example if I wanted to write down a note, I could create a Word document, Excel Spreadsheet, or one of the scribble pad notes. My mind would weigh the pros and cons, then I'd finally just decide to write it down on paper. On my iPhone I just have Notes, and I use them all the time now.</p><p><strong>Cut the Unnecessary</strong></p><p>I was kind of surprised that the iPhone didn't come with a Todo list. This seems like such an essential smartphone application. Yet I realized that wait a minute, I never used the Todo list on my old phone. My problem was that I would put things on my Todo list that were hard to remember, but I could never remember to look at my Todo list. I tried a few web based Todo lists, but ended up with a much better solution. Now when I need to remember to do something like get new tags for my car, I put it in my Calendar. This way I set a date and time that I need to do it and it reminds me that I need to do it. Not only that, but now I have a history of when I did things.</p><p>The iPhone engineers were able to step back and look at how people will really use this phone, and didn't dilute it with the unecessary 10%. So many software companies are afraid to think for their customers, when in the end, that's what the customers are paying for.</p><p>Simplicity gives way to fluid and natural actions by eliminating distractions and only focusing on what you really need. I couldn't put it into words like that before.</p> Fri, 28 Dec 2007 19:38:00 -0600 http://garbageburrito.com/blog/entry/9190/lessons-learned-from-the-iphone http://garbageburrito.com/blog/entry/9190/lessons-learned-from-the-iphone Doodlekit gets TechCrunched <p>Yesterday TechCrunch did a <a href="http://www.techcrunch.com/2007/11/30/doodlekit-brings-advanced-functionality-to-easy-website-creation/">writeup on Doodlekit</a>. The article focused on how our advanced features set us apart from our competitors.</p><p>My only complaint was the misnomer that <a href="http://doodlekit.com">Doodlekit</a> is late to the party. According to some of the comments, you don't actually exist until you've been posted on TC. The fact is that we started on DK almost 2 years ago, and did our first major release last year. Blah blah, I know this sounds like pathetic drivel, but hey, I needed to get it out there.</p><p>Of course it doesn't bother me too much, because I'll be laughing all the way to the bank! HA! I expected a lot of traffic, but I didn't expect that over 600 sites would be created since the article was posted. As the article rolled to the bottom of the TC homepage, it showed up on the del.icio.us home page.</p><p>This was also a good test of the server. Everything held up quite well.</p><p>This has been a fun 24 hours. I hope it continues.</p> Sat, 01 Dec 2007 15:56:00 -0600 http://garbageburrito.com/blog/entry/3762/doodlekit-gets-techcrunched http://garbageburrito.com/blog/entry/3762/doodlekit-gets-techcrunched Doodlekit is now more Awesomer than ever <p>Last Saturday we finally deployed the latest <a href="http://doodlekit.com" onclick="window.open(this.href);return false;" onkeypress="window.open(this.href);return false;">Doodlekit</a> release. So far I'm really happy with how things are going. Our signups have already quintupled, and everything is running smooth as usual.</p><p>Here's what I'm excited about.</p><p><strong>Free as in Beer</strong></p><p>We now have a completely free <a href="http://doodlekit.com" onclick="window.open(this.href);return false;" onkeypress="window.open(this.href);return false;">Doodlekit</a> plan that still has lots of great features like a Blog, Photo Album, and Content Pages. Hear that Railers? You can have a <a href="http://doodlekit.com" onclick="window.open(this.href);return false;" onkeypress="window.open(this.href);return false;">Ruby on Rails hosted Blog for free</a>!</p><p><strong>New Editor - XStandard</strong></p><p>If you've done any research into online WYSIWYG editors, you should know that <a href="http://xstandard.com" onclick="window.open(this.href);return false;" onkeypress="window.open(this.href);return false;">XStandard</a> is the cream of the crop. It spits out completely W3C compliant HTML, and uses styles instead of fonts. Since it's a browser plugin, and doesn't use the browsers built in content editor, it's a lot more stable and predictable. <br /> <br />Not only that, but we made it a lot better. The editor is not only aware of what color theme you're using, but also what you're editing. So if I start editing a notes box on my sidebar, the width of the editable area will be the same, and the background color will match.</p><p>I also built out all the backend services, so when you're inserting an image, you can browse your Doodlekit Photo Albums, or upload directly from your computer. Sorry to ramble, but I'm really psyched about this.</p><p><strong>Shopping Cart</strong></p><p>In true DK style, the new <a href="http://doodlekit.com">Shopping Cart</a> is really simple, yet slick as all hell. It takes just a few minutes to setup, and integrates directly with Paypal. <a href="/shop/2">Check out my demo</a>.</p><p><strong>Performance</strong></p><p>Dude, you don't even know.....</p><p>I almost crapped my pants when I checked the logs and saw 200+ requests per second. Then I did crap my pants when I started up memcached, and saw 300+ requests per second. And that's per process across 4 processes. More on that to come...</p><p>&#160;</p><p>&#160;</p> Tue, 16 Oct 2007 16:31:00 -0500 http://garbageburrito.com/blog/entry/1046/doodlekit-is-now-more-awesomer-than-ever http://garbageburrito.com/blog/entry/1046/doodlekit-is-now-more-awesomer-than-ever Mysql Insert Delayed / MyISAM Migrations <p>For reasons I wont get into, I'm logging page hits to the database in <a href="http://doodlekit.com">doodlekit</a>.&#160; The performance impact really hasn't been that bad, but I think it will start to show over time.&#160; I've been looking for some sort of fire-and-forget function for a long time.&#160; I thought about threads, message queues, or even a Ruby ESB.&#160; But I finally found a much simpler option. &#160;</p><p>Mysql has a <a href="http://dev.mysql.com/doc/refman/5.0/en/insert-delayed.html">DELAYED</a> modifier for the INSERT function.&#160;&#160; Basically the client just sends the statement and returns right away.&#160; The statement goes into a queue, and will insert whenever Mysql decides to.&#160; If there are many statements at the same time, it will batch insert them.&#160; Obviously you would only want to use this in very specific circumstances.&#160; It's perfect for me since I don't rely on the record being there immediately. &#160;&#160;</p><p>It's very simple...</p><pre> &#160;&#160;&#160; INSERT DELAYED INTO requests (ip, path, .....) VALUES ('127.0.0.1', '/home', ....) </pre><p>I couldn't find any plugins to do this, and of course I'm to lazy to write my own.&#160; I just did a raw sql execute with the proper Sql Injection protection.&#160;&#160;</p><p>One important thing to mention is that the DELAYED modifier requires the MyISAM table engine, and rails uses InnoDB by default.&#160; You'll have to convert the table...</p><pre> &#160; def self.up<br />&#160;&#160;&#160; execute(&quot;ALTER TABLE web_requests ENGINE=MyISAM&quot;)<br />&#160; end </pre><p>And to top it all off, by default the type of Mysql storage engine you're using wont be copied to your test database when it does db:test:prepare.&#160; When you run a unit/functional test, rails will export your development database into the schema.rb file.&#160; It doesn't have the capacity to handle special options like engine types, so that's all lost.&#160; To get around this, simply use the sql schema export type.&#160;&#160;</p><p>Put this in environment.rb</p><pre> &#160;&#160;&#160; config.active_record.schema_format = :sql&#160; </pre><p>I did some benchmarking with INSERT DELAYED, and when inserting 1000 to 100,000 records, there's a 3 to 4 fold speed increase.&#160; Pretty snappy. <br /></p> Thu, 16 Aug 2007 00:09:00 -0500 http://garbageburrito.com/blog/entry/788/mysql-insert-delayed-myisam-migrations http://garbageburrito.com/blog/entry/788/mysql-insert-delayed-myisam-migrations Heartbeat / Doodlekit Release <p>Lots going on in my world right now.&#160; Between finally accepting Assurant's offer, trying to get back into fixing up the house, and <a href="http://doodlekit.com" target="_blank">doodlekit</a>, I've not got much time for blogging.<br /><br />Speaking of doodlekit, we've got a big release in the works right now.&#160; Though we didn't plan for it to be a formal release, a lot of things sort of depend on others.&#160; There's lots of new stuff to be excited about, but I have three favorites.&#160;</p><p>1. Doodlekit will be Free!&#160; We've finally reached a point where we are able to support a totally free plan.&#160; It will be limited of course, but it still beats the pants off a lot of other tools.<br /><br />2. Shopping Cart.&#160; The Advanced plan will have a super simple DK style shopping cart that hooks right into Paypal. I can't wait to show this one off.<br /><br />3. New Editor.&#160; We've chosen <a href="http://xstandard.com" target="_blank">XStandard</a> as our new WYSIWYG tool.&#160; This is one of the greatest pieces of software I've ever landed my hands on.&#160; After the release I'll be talking more about this and the integration process.<br /><br />Check out <a href="http://doodlekit.com/blog/entry/756/major-new-release-for-doodlekit-coming-soon" target="_blank">Heath's post</a> for more details.<br /><br />It's all going down late September, so be ready.</p> Tue, 07 Aug 2007 14:45:00 -0500 http://garbageburrito.com/blog/entry/760/heartbeat-doodlekit-release http://garbageburrito.com/blog/entry/760/heartbeat-doodlekit-release Slideshow / Clearing All Javascript Timers <p>I decided to whip up a slideshow for the photo albums, since a few people have asked for it.&#160; First I thought about finding a flash one, but I knew that it would probably ask for a simple directory of images, and <a href="http://doodlekit.com">doodlekit</a> doesn't store images that way.&#160; So I figured I could just use Ajax and some Scriptaculous effects instead.&#160; The <a href="/album/slideshow/248" onclick="window.open(this.href,'slideshow','height=535,width=565');return false;">end result is pretty slick</a><br /><br />One thing I had to do to make it look so good is preload the next image, so you can't see it loading when the last one is fading.&#160; To do this I have a temporary div that sits over the main image div.&#160; I copy image A into the temp div, load image B in the main div, then fade the temp div.&#160; This requires me to use four different timers for various purposes to make everything look smooth.<br />&#160; <br />However, I ran into a problem when I added the previous and next links.&#160; When you clicked on either, it loaded the proper image fine but the timers where still running.&#160; This started a chain reaction of all kinds of crap. &#160;<br /><br />I needed a way to stop the timers, but I was using Rail's Javascript helpers which meant I couldn't just get the timer's id and call clearTimeout.<br /><br />Instead I came up with a cool way to clear all the timers in one swoop.&#160; Basically I override the setTimeout method and store each timer id in an array.&#160; Then when I need to I just loop through the array and clear them all.<br /></p><pre> &#160;&#160;&#160; timers = new Array();<br />&#160;&#160;&#160; oldSetTimeout = window.setTimeout;<br />&#160;&#160;&#160; window.setTimeout = function(code, interval) {<br />&#160;&#160;&#160;&#160;&#160; timers.push(oldSetTimeout(code, interval));<br />&#160;&#160;&#160; }<br />&#160;&#160; &#160;<br />&#160;&#160;&#160; function resetTimeouts() {<br />&#160;&#160;&#160;&#160;&#160; timers = new Array();<br />&#160;&#160;&#160; }<br />&#160;&#160; &#160;<br />&#160;&#160;&#160; function clearTimeouts() {<br />&#160;&#160;&#160;&#160;&#160; for (var i= 0;i &lt; timers.length; i++) {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; clearTimeout(timers[i]);<br />&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160;&#160;&#160; resetTimeouts();<br />&#160;&#160;&#160; } </pre><p><br />I know this isn't totally elegant, but I think it's a good solution to a sticky problem.&#160; To use, just include this code and call clearTimeouts whenever you want.&#160; I extracted resetTimeouts so it could be called after each slide transition, and so the timer array didn't just keep growing.<br /><br /></p> Thu, 31 May 2007 16:07:00 -0500 http://garbageburrito.com/blog/entry/555/slideshow-clearing-all-javascript-timers http://garbageburrito.com/blog/entry/555/slideshow-clearing-all-javascript-timers Rails: Capturing HTML in Erb Blocks <p>I've been <a href="http://garbageburrito.com/blog/entry/464/embedded-blocks-for-rails-erb-tag-voodoo">having fun with blocks and HTML</a> lately, but I realized I kind of glazed over a very important, yet enigmatic piece of the puzzle.&#160; How do you capture the block of HTML, and append/prepend stuff to it?&#160; To figure this out I had to dig a little.<br /><br />I knew that the new and improved form_tag helper had to have this magic in it, and sure enough....<br /><br /></p><pre> if block_given?<br />&#160; content = capture(&amp;block)<br />&#160; concat(tag(:form, html_options, true) + method_tag, block.binding)<br />&#160; concat(content, block.binding)<br />&#160; concat(&quot;&lt;/form&gt;&quot;, block.binding)<br />else<br />&#160; tag(:form, html_options, true) + method_tag<br />end </pre><p><br />First it checks to see if a block was provided.&#160; If so we use the capture helper to grab the HTML from the enclosed block.&#160; Capturing it means it ain't going to print to the screen itself.&#160; Using the concat helper, we can &quot;bind&quot; new HTML to the output buffer.&#160; This is a little confusing, because the arguments are kind of backwards, IMO.&#160; Think of block.binding as the HTML page, and the first argument as the shtuff you want to append to it.<br /><br />If there's no block, it just creates the first half of the form tag. &#160;<br /><br />Here's a simple example.&#160; We needed to wrap all the forms in <a href="http://doodlekit.com">Doodlekit</a> with a div, for HTML compliance or some other crazy CSS Wizardy.&#160; So I overwrote the form_tag helper like this...<br /><br /></p><pre> alias old_form_tag form_tag<br /><br />def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &amp;proc)<br />&#160; if block_given?<br />&#160;&#160;&#160; content = capture(&amp;proc)<br />&#160;&#160;&#160; concat('&lt;div class=&quot;form_tag&quot;&gt;', proc.binding)<br />&#160;&#160;&#160; concat(old_form_tag(url_for_options, options, parameters_for_url), proc.binding)<br />&#160;&#160;&#160; concat(content, proc.binding)<br />&#160;&#160;&#160; concat('&lt;/form&gt;&lt;/div&gt;', proc.binding)<br />&#160; else<br />&#160;&#160;&#160; old_form_tag(url_for_options, options, parameters_for_url)<br />&#160; end<br />end </pre><p><br />I alias the original form_tag method, so I can still use it.&#160; If a block is given I capture the HTML.&#160; Print the div, print the form, print the captured content, print the end form, print the end div.<br /><br />It's a little less than straight forward at first, but once you've got it your good to rock some funky erb beats.</p> Tue, 01 May 2007 16:24:00 -0500 http://garbageburrito.com/blog/entry/484/rails-capturing-html-in-erb-blocks http://garbageburrito.com/blog/entry/484/rails-capturing-html-in-erb-blocks State of The Doodle: New Site Launch <p><a href="http://doodlekit.com/blog/entry/480/doodlekit-site-launch-tymber-ultra-sports-lounge">We just launched</a> our latest <a href="http://doodlekit.com">custom Doodlekit site</a>, <a href="http://tymberks.com">Tymber Ultra Sports Lounge</a>.&#160; <a href="http://heathbits.com">Heath</a> really keeps out-doing himself every time.&#160; This is a really good example of how flexible the tool is.&#160; For example the boxes on the left for the different nights are just Notes in the sidebar.&#160; The events calendar is just the blog. &#160;<br /><br />Anyway, I thought this would be a good excuse to document how things are going right now.&#160; If I had to choose one word it would be &#8220;fast&#8221;. &#160;<br /><br />Our trial signups are steady, and our business account signups have increased significantly.&#160; We&#8217;ve got several irons in the fire with businesses looking for sites, as well as other vendors looking for pseudo partnerships.&#160; We just got a call today with a large university that&#8217;s planning on hooking up.&#160;&#160; I can attribute a lot of the increased activity to outstanding google search rankings.<br /><br />I&#8217;m working on some really cool Addons, as well as many small enhancements here and there.&#160; We&#8217;ve turned the layout manager into a bad-ass mother-effer, with more improvements soon to come.&#160; One of the major improvements in the pipe-line is a new and improved WYSIWYG tool.&#160; TinyMCE is great, but we have definitely outgrown it.<br /><br />There&#8217;s a super special announcement we plan to make in a month or two that will hopefully open a new chapter for us.<br /><br />Exciting times, thanks for reading.<br /><br /></p> Mon, 30 Apr 2007 17:07:00 -0500 http://garbageburrito.com/blog/entry/481/state-of-the-doodle-new-site-launch http://garbageburrito.com/blog/entry/481/state-of-the-doodle-new-site-launch Embedded Blocks for Rails Erb Tag Voodoo Whilst <a href="http://garbageburrito.com/blog/entry/462/upgrading-doodlekit-to-rails-123-torture-hell-ride">upgrading doodlekit</a>, I found a cool trick that I&rsquo;ll probably start to use a lot.&nbsp; I already posted about how to <a href="http://garbageburrito.com/blog/entry/8/using-ruby-blocks-for-custom-rails-tags">use blocks to make sort of custom tag in Rails</a>.&nbsp; I recently used this to clean up some html that had somehow creeped it&rsquo;s way onto every view in <a href="http://doodlekit.com">Doodlekit</a> .&nbsp; I abstracted this view using a partial and a helper, so I can say&hellip;.<br /><br /><font face="courier new,courier">&nbsp; &lt;% content_headers &ldquo;Blog&rdquo; do %&gt;<br />&nbsp;&nbsp;&nbsp; Some funky jazz<br />&nbsp; &lt;% end %.</font><br /><br />&hellip;and it will wrap the content with an set of divs.&nbsp; Pretty basic. <br /><br /><font face="courier new,courier">&nbsp; def content_headers(header = &#39;&#39;, sub_header = &#39;&#39;, options = {}, &amp;block)<br />&nbsp;&nbsp;&nbsp; if !options.has_key?(:if) || options[:if]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content_body = capture(&amp;block)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; concat(render(:partial =&gt; &#39;shared/content_headers&#39;, <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :locals =&gt; { :body =&gt; content_body, :header =&gt; header, :sub_header =&gt; sub_header }), <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; block.binding)<br />&nbsp;&nbsp;&nbsp; end</font><br />&nbsp; end<br /><br />and the partial<br /><br /><font face="courier new,courier">&nbsp; &lt;!-- Lots of Heath&#39;s funky divs.... --&gt;<br />&nbsp; &lt;div&gt;<br />&nbsp;&nbsp;&nbsp; &lt;h2&gt;&lt;%= header %&gt;&lt;/h2&gt;<br />&nbsp;&nbsp;&nbsp; &lt;% unless sub_header.blank? -%&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;h4&gt;&lt;%= sub_header %&gt;&lt;/h4&gt;<br />&nbsp;&nbsp;&nbsp; &lt;% end -%&gt;<br />&nbsp; &lt;/div&gt;<br /><br />&nbsp; &lt;%= body %&gt;<br /><br />&nbsp; &lt;!-- more divs and stuff.... --&gt;</font><br /><br />But then I needed the ability to pass a larger block of html to the helper, but I didn&rsquo;t feel comfortable passing as an argument, ala<br /><br /><font face="courier new,courier">&nbsp; &lt;% content_headers &ldquo;Blog&rdquo;, link_to(&ldquo;Blog&rdquo;, :action =&gt; &lsquo;index&rsquo;) + &ldquo; &amp;gt; #{@entry.title}&ldquo;do %&gt;</font><br /><br />So what I did was create another helper that looks like this.<br /><br /><font face="courier new,courier">&nbsp; &lt;% content_headers &ldquo;Blog&rdquo; do %&gt;<br />&nbsp;&nbsp;&nbsp; &lt;% breadcrumb do %&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;%= link_to(&ldquo;Blog&rdquo;, :action =&gt; &lsquo;index&rsquo;) %&gt; &amp;gt;&nbsp; &lt;%= @entry.title} %&gt;<br />&nbsp;&nbsp;&nbsp; &lt;% end %&gt;<br />&nbsp;&nbsp;&nbsp; Some funky jazz<br />&nbsp; &lt;% end %&gt;</font><br /><br />All the breadcrumb helper does is capture the block and set it to an instance variable called @breadcrumb.<br /><br /><font face="courier new,courier">&nbsp; def breadcrumb(&amp;block)<br />&nbsp;&nbsp;&nbsp; @breadcrumb = capture(&amp;block)<br />&nbsp; end<br /></font><br />Then I can display the breadcrumb instance variable anywhere I want in the content_headers partial.<br /><br /><font face="courier new,courier">&nbsp; &lt;!-- Lots of Heath&#39;s funky divs.... --&gt;<br />&nbsp; &lt;div&gt;<br />&nbsp;&nbsp;&nbsp; &lt;% if @breadcrumb -%&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div class=&quot;bread_crumb&quot;&gt;&lt;%= @breadcrumb %&gt;&lt;/div&gt;<br />&nbsp;&nbsp;&nbsp; &lt;% end -%&gt;<br />&nbsp;&nbsp;&nbsp; &lt;h2&gt;&lt;%= header %&gt;&lt;/h2&gt;<br />&nbsp;&nbsp;&nbsp; &lt;% unless sub_header.blank? -%&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;h4&gt;&lt;%= sub_header %&gt;&lt;/h4&gt;<br />&nbsp;&nbsp;&nbsp; &lt;% end -%&gt;<br />&nbsp; &lt;/div&gt;<br /><br />&nbsp; &lt;%= body %&gt;<br /><br />&nbsp; &lt;!-- more divs and stuff.... --&gt;</font><br /><br />One way I&rsquo;ll probably start to use this is to be able to define a small block of HTML in the view, that&rsquo;s extracted and placed into the outer layout.<br /><br />Lot&rsquo;s of possibilities here.<br /><br /> Mon, 23 Apr 2007 14:41:00 -0500 http://garbageburrito.com/blog/entry/464/embedded-blocks-for-rails-erb-tag-voodoo http://garbageburrito.com/blog/entry/464/embedded-blocks-for-rails-erb-tag-voodoo Upgrading Doodlekit to Rails 1.2.3 - Torture Hell Ride <p>I&#8217;ve been putting it off for a while, but I finally got around to upgrading <a href="http://doodlekit.com">Doodlekit</a> to Rails 1.2.3.&#160; I planned on documenting the upgrade, so that I could post it here and possibly help some people out.&#160; However, it turned into such a shitstorm, I had to just go monk and get it done.<br /><br />Here are some highlights.<br /><br />1.&#160; My main problem was with engines.&#160; <a href="http://doodlekit.com">Doodlekit</a> has become so huge that I implement the Addons as engines to help keep things manageable.&#160; I had a lot of weird errors at the beginning, and I finally figured out it was because of the changes in the way engines work.&#160; Apparently a lot of the engine code has been sucked up into rails core, and the actual engines implementation is pretty light. &#160;<br /><br />At first I upgraded the engines plugin using script/plugin, but after getting more errors I finally just deleted the engines dir, and installed it fresh.&#160; That cleared up most of my problems. &#160;<br /><br />2.&#160; There were a lot of places where I was auto-loading classes that I need to manually require.&#160; In this case it was just a matter of running the tests and finding each one.&#160; Not too bad.<br /><br />3.&#160; Form tags.&#160; Friggin frackin form tags! When I finally got my tests to actually run, all I could see were deprecation warnings.&#160; Most of them were pretty easy fixes, however I wish I would have counted how many form tags I had to fix.&#160; I couldn&#8217;t stand all the warnings, so I buckled down and replaced all the start and end_form_tag&#8217;s with the new sexier block method.&#160; This did give me a chance to clean up some other things that were bothering me.&#160; I mean as long as I was touching every view in my codebase.<br /><br />4.&#160; I finally got all my tests running cleanly, and did a manual walk through.&#160; Not so fast Billy Ray.&#160; My routing wasn&#8217;t working at all.&#160; See in order to support the multi-site environment locally, I use a subdirectory context, like http://localhost:3000/default/blog, http://localhost:3000/garbageburrito/blog.&#160; I have a special routing for development mode that pulls that directory name out.&#160; However Rails new routing system didn&#8217;t seem to like that at all.&#160; &#160;<br /><br />I figured it would be a lot of trouble to get that all running properly, so I just scrapped it.&#160; Now I can test a single site without any special setup, and I can test multi-site by adding sub-domains to my hosts file.&#160; I might try something slicker down the road, but I&#8217;m tired now.<br /><br />5.&#160; I did the walkthrough, found a few bugs here and there, and was ready for deployment.&#160; At first I just did a normal rails upgrade through gem, and cap&#8217;d it.&#160; Mongrel wouldn&#8217;t start cause it couldn&#8217;t find &#8216;gem&#8217;.&#160; So after some frustration I figured out I needed to upgrade rubygems from 0.8.11 to 0.9.2.&#160; I tried &#8216;gem install rubygems-update&#8217; first, but &#8216;gem &#8211;v&#8217; still came up with the old version.&#160; After some digging I found out I needed &#8216;gem update &#8211;system&#8217;, which did the trick. &#160;<br /><br />However it still gave me the require_gem deprecation warning.&#160; I figured this was cause I still had ruby 1.8.4.&#160; For some reason yum couldn&#8217;t find the latest version of ruby, so I compiled it from source, removed it from yum, and re-installed all the gems.<br /><br />Running swimmingly now.<br /><br />Like I said, I hoped that would have been more instructional than narrative, but it just wasn&#8217;t that clean.&#160; Please oh please don&#8217;t make me go through this for 2.0.<br /><br /></p> Mon, 23 Apr 2007 10:52:00 -0500 http://garbageburrito.com/blog/entry/462/upgrading-doodlekit-to-rails-123-torture-hell-ride http://garbageburrito.com/blog/entry/462/upgrading-doodlekit-to-rails-123-torture-hell-ride Rails: Super Cool Simple Column Sorting <p>Of all the things I hate doing, column sorting has to be top on my list.&#160; It always seems to be more difficult than it should be.&#160; Here's my take on a really simple way to implement column sorting for your Rails application.&#160; I call it Super Cool Simple Column Sorting, or SCSCS.&#160; That's pronounced 'ssssssss'.<br /><br />The first part is the HTML helper.</p><pre> &#160; def sort_link(title, column, options = {})<br />&#160; &#160; condition = options[:unless] if options.has_key?(:unless)<br />&#160;&#160;&#160; sort_dir = params[:d] == 'up' ? 'down' : 'up'<br />&#160; &#160; link_to_unless condition, title, request.parameters.merge( {:c =&gt; column, :d =&gt; sort_dir} )<br />&#160; end </pre><p>&#160;&#160; &#160;<br />It takes the title you wish to display, the name of the column you wish to sort, and any extra options you wish to add to the link.&#160; You can also pass an :unless flag to turn the link on or off.&#160; Put this in a helper somewhere, maybe application_helper.rb.<br /><br />To use it...</p><pre> &#160; &lt;%= sort_link 'Company', :company_name %&gt; </pre><p>&#160; <br />This will default to sorting up, or ASC, and will toggle up or down automatically.<br /><br />Now you just need the SQL helper.<br /></p><pre> &#160; def sort_order(default)<br />&#160; &#160;&#160; &#160;&quot;#{(params[:c] || default.to_s).gsub(/[\s;'\&quot;]/,'')} #{params[:d] == 'down' ? 'DESC' : 'ASC'}&quot;<br />&#160; end </pre><p>&#160;&#160; &#160;<br />Put this somewhere the controller can see it, maybe application.rb.&#160; Then just call it in the query in your controller.<br /></p><pre> &#160; def list<br />&#160;&#160;&#160; @applications = Application.find(:all, :order =&gt; sort_order('created_at'))<br />&#160; end </pre><p>&#160; <br />The sort_order method takes the default column you wish to sort on. &#160;<br /><br />I really like this simple helper based approach.&#160; It's not quite as slick as making a plugin, but it's easier to tweak it for different cases, and less than 10 lines. <br /></p> Thu, 19 Apr 2007 13:17:00 -0500 http://garbageburrito.com/blog/entry/447/rails-super-cool-simple-column-sorting http://garbageburrito.com/blog/entry/447/rails-super-cool-simple-column-sorting A Case for Architectural Simplicity <p>I would like to share an experience that is all too typical at my day job, in hopes that it may stop someone from saying, &quot;Just one more framework isn't gonna hurt&quot;.<br /><br />I'm working on a sales leading tracking system which has been handed down to unsuspecting developers for around 3 years.&#160; The architecture consists of Java, Portal, Struts, Tiles, Spring, Hibernate, EJB's, and probably some other stuff I'm forgetting.&#160; I mainly work on the front end Portal/Struts stuff.<br /><br />Today I was working on the new phase, and I needed to find a JSP to make some simple formatting changes.&#160; I could navigate to the page through Portal, but actually finding the JSP wasn't so easy.&#160; I thought I knew what file it was, because a portion of the html matched, but there was a large piece missing.&#160; So I started digging.<br /><br />The first problem is that Portal has it's own completely unreadable URL mumbo jumbo.&#160; So there's know easy way to say, &quot;this page, is using this action/JSP/whatever&quot;.&#160; So I had to look at the form from whence it came.&#160; The form pointed to a struts action, which I proceeded to look for in the eleven struts configuration files contained in this project.&#160; It wasn't in the first two files that actually made since, so I had to do a file search for it.&#160; I found the configuration, however it just pointed to another struts action, which luckily was in the same file.<br /><br />That Struts action pointed to a Tiles (Templating Framework) configuration, which I had to find amongst thirteen config files.&#160; Of course one configuration points to another, which finally points to a JSP! However, it's totally not what I was looking for.&#160; Frustration!&#160; I have no idea how this happened, but I needed a new tactic.<br /><br />I took a blurb of copy from the rendered HTML and searched for that.&#160; I figured it would be in the ApplicationResources.properties file, which for those unfamiliar is a place where you store text labels with unique identifiers, used for i18n.&#160; I got the key for this text and searched for that in JSP's.&#160; The JSP I find is the one I thought it might be in the first place.&#160; I double check, and this is definitely not it. Anger!<br /><br />At this point feel like I'm getting very lost, and I'm struggling to remember what I was looking for in the first place.&#160; This time I take the name of a text field from the rendered HTML and search for it.&#160; I find a JSP, and it actually looks like the right one this time.&#160; However there's another problem, there are two jsp files, batch_categorize.jsp and batchCategorize.jsp.&#160; %$%#^&amp;@*!&#160; MADNESS!&#160; What the crap, man?!&#160; To make a long story longer, I traced the file back through two tiles configs, and a struts action, and confirm that it's the underscored version I'm looking for, even though every other JSP in the project is camel cased.<br /><br />Of course by this time I've totally forgotten what the heck I was trying to do in the first place.<br /><br />Don't let this happen to you. <br /><br /></p> Tue, 17 Apr 2007 16:24:00 -0500 http://garbageburrito.com/blog/entry/441/a-case-for-architectural-simplicity http://garbageburrito.com/blog/entry/441/a-case-for-architectural-simplicity TinyMCE GZip Compressor Ruby on Rails Plugin - Take 3 <p>One of the coolest <a href="http://doodlekit.com" target="_blank">Doodlekit</a> blogs I've seen is <a href="http://tallmatt.com/" target="_blank">Tall Matt's Travels</a>, where Matt Olson chronicles is non-stop trip around the world.&#160; Matt was tired of corporate america (can't blame him since he worked at Cerner), and left it all to pursue his dream.&#160; It's quite amazing actually. &#160;<br /><br />Anyway, Matt has been blogging through various Internet cafe's, and recently had a problem with the WYSIWYG.&#160; The problem was that the particular browser he was using did not support gzip compression.&#160; I assumed we where safe from this problem, but then Matt had to go to Antartica!<br /><br />This is good though, cause it force me to finish the last piece of the TinyMCE Gzip Compressor.&#160; Before I couldn't support conditional Gzip compression, since Rails' caching stuff doesn't allow after_filter's.&#160; I ended up having to copy the caching code and massage in some gzip stuff to get it to work right.<br /><br />Here it is.<br /><br /><a href="http://garbageburrito.com/media/download/download_file/22/tiny_mce_gzip.zip">TinyMCE GZip Compressor Ruby on Rails Plugin</a> <br /><br />To install it, extract the plugin to vendor/plugins.&#160; The path should look like RAILS_ROOT/vendor/plugins/tiny_mce_gzip.<br /><br />Add the following line to routes.rb<br /></p><pre> TinyMceGzip::Routes.add_routes </pre><p>&#160;&#160; &#160;<br />Change your tiny_mce javascript include to the following.<br /></p><pre> &lt;%= javascript_include_tag(&quot;tiny_mce/tiny_mce_gzip&quot;) %&gt; </pre><p>&#160;</p><p>This plugin assumes that TinyMCE is installed in /public/javascripts/tiny_mce. &#160;<br /><br />I've tested this in Rails 1.1.6 and 1.2.3.</p><p>I've also created a separate page specifically for the plugin.&#160; If you're going to link to it, please use this url.</p><p><a href="http://garbageburrito.com/home/tinymce_gzip_compressor_ruby_on_rails_plugin">http://garbageburrito.com/home/tinymce_gzip_compressor_ruby_on_rails_plugin</a> &#160;</p> Thu, 05 Apr 2007 14:28:00 -0500 http://garbageburrito.com/blog/entry/419/tinymce-gzip-compressor-ruby-on-rails-plugin-take-3 http://garbageburrito.com/blog/entry/419/tinymce-gzip-compressor-ruby-on-rails-plugin-take-3 A Mac-esque Rails Development Environment on Windows <p><a href="http://garbageburrito.com/album/image/4049" target="_blank"><img align="right" alt="" class="right" height="125" src="/media/image/image/4049/mac_rails_on_windows.png" width="200" /></a> Here's the deal, as I've said before, I like Windows and I love Vista.&#160; Mac's certainly are beautiful; but as of yet <a href="http://www.apple.com/getamac/works.html" target="_blank">&quot;It just works&quot;</a> is not convincing enough for me.&#160; However, any Railer will tell you that the holy grail of Rails editors is TextMate, which of course is Mac only.&#160; I've come to believe that TextMate is not just the favorite Rails editor because a titanium Mac Book looks good with DHH's blazer, but there is also a common philosophy between them.&#160; A simple elegant language should have a simple and elegant editor.&#160; So therein lies my delima.&#160; <br /></p><p>Throughout the creation of my <a href="http://doodlekit.com/home/website_builder">website builder</a>, <a href="http://doodlekit.com">doodlekit</a>, many different editors/environments have come and gone.&#160; I've been eyeing <a href="http://www.e-texteditor.com/" target="_blank">e</a> (three times fast), and it's really shaping up to be a very nice TextMate clone for Windows.&#160; Even before that <a href="http://garbageburrito.com/blog/entry/74/console-2-the-windows-comand-prompt-replacement" target="_blank">I fell in love with Console</a>, which makes the Windows command line pleasurable.&#160; And cygwin is of course more than capable of <a href="http://garbageburrito.com/blog/entry/240/rails-development-environment-under-cygwin" target="_blank">running a Rails environment.</a> &#160; All the pieces have fallen into place, and I have a recipe for a very nice Rails development environment on Windows.<br /><br /><span style="font-weight: bold">Notes:</span>&#160; I did this on Windows Vista Ultimate 64-bit, but I think it should be the same for all Windows versions.&#160; This guide assumes you have a novice understanding of Linux/cygwin.&#160; And finally, I&#8217;ll leave choosing and installing a database to you.<br /><br /><span style="font-weight: bold">Step 1 - Install e</span><br /><br /><a href="http://www.e-texteditor.com/download/e_beta_setup.exe" target="_blank">http://www.e-texteditor.com/download/e_beta_setup.exe</a> <br /><br />Be sure to pay for it once you've decided to keep using it.<br /><br /><span style="font-weight: bold">Step 2 - Install cygwin</span><br /><br />E relies on cygwin for some of the bundles, which is ok since our Rails environment will also be running in cygwin.&#160; Start e after it's installed.&#160; If a dialog titled &quot;Update Cygwin&quot; doesn't popup right away, click on some of the bundles till it does.<br /><br />Select the &quot;Manual&quot; radio button and click &quot;Next&quot;.<br /><br />The default options should be good until you get to &quot;Select Packages&quot;. Make sure Ruby, Subversion, and any other Packages you might need are selected to install.<br /><br />Finish the cygwin installation<br /><br /><span style="font-weight: bold">Step 3 - Install Console</span><br /><br />Console is probably my favorite thing ever.&#160; It's a command prompt replacement for Windows with tabs, transparency and other funky jazz.<br /><br /><a href="http://sourceforge.net/projects/console/" target="_blank">http://sourceforge.net/projects/console/</a> <br /><br />Make sure you grab the very latest build.<br /><br />You'll want to setup cygwin as a Console Tab.&#160; Just go into the settings and click &quot;Add&quot; under &quot;Tabs&quot;.&#160; Use the following as the &quot;Shell&quot;.<br /><br /><span style="font-family: courier new,courier">&#160;&#160;&#160; c:\cygwin\bin\bash --login -i</span><br />&#160;&#160;&#160; <br />If you want it to automatically open cygwin when you start Console, just move it to the top of the Tabs list.<br /><br /><span style="font-weight: bold">Step 4 - Install Ruby Gems</span><br /><br /><a href="http://rubyforge.org/frs/?group_id=126&amp;release_id=9501" target="_blank">http://rubyforge.org/frs/?group_id=126&amp;release_id=9501</a> <br /><br />Download, un-tar, and run the setup ruby script.&#160; You'll probably get an error that says &quot;No such file to load -- ubygems (LoadError)&quot;.&#160; Just run the following command to get around it.<br /><br /><span style="font-family: courier new,courier">&#160;&#160;&#160; unset RUBYOPT</span><br /><br /><span style="font-weight: bold">Step 5 - Install Gems</span><br /><br />Now just install rails and all the other gems you need, as you normally would.&#160; Keep in mind that you are in cygwin, so do NOT choose the win32 versions of the gems<br /><br /><span style="font-family: courier new,courier">&#160;&#160;&#160; gem install rails --include-dependencies</span><br />&#160;&#160;&#160; <br /><span style="font-weight: bold">Step 6 - Setup e Alias</span><br /><br />This last step is optional.&#160; If you're familiar with TextMate, you can open it from the command line use the 'mate' command, but more importantly you can open the current directory as a project by using 'mate .'.&#160; e does the same thing, however when you do this in cygwin, it retains control of the shell instead of running it in the background.&#160; I came up with a way to handle this, but Charles Roper in the e forums <a href="http://www.e-texteditor.com/forum/viewtopic.php?t=840" target="_blank">had a better way</a>.<br /><br />First make sure e is in your $PATH, either through the Windows or cygwin environment variables.&#160; You can test this by just typing 'e' from cygwin and hiting enter.&#160; To make e run in the background add this to your .bashrc file.&#160; <br /><br /><span style="font-family: courier new,courier">&#160;&#160;&#160; alias e='cygstart e'</span><br /><br /><span style="font-weight: bold">Step 7 &#8211; Test it Out</span><br /><br />Open Console, and make sure you&#8217;re on a cygwin tab.&#160; Create a rails project as normal.<br /><br /><span style="font-family: courier new,courier">&#160;&#160;&#160; rails burrito</span><br /><br />Go into the new directory and open e.<br /><br /><span style="font-family: courier new,courier">&#160;&#160;&#160; cd burrito<br />&#160;&#160;&#160; e .</span><br /><br />This will open e with your new project loaded into the project explorer.&#160; <br /><br />That&#8217;s it!&#160; Have fun.<br /><br /></p> Mon, 26 Mar 2007 16:29:00 -0500 http://garbageburrito.com/blog/entry/391/a-macesque-rails-development-environment-on-windows http://garbageburrito.com/blog/entry/391/a-macesque-rails-development-environment-on-windows Doodle Design Frenzy! <p>While I've been struggling to find time, Heath has been going crazy re-designing stuff left and right.&#160;</p><p>He started with his own portfolio, <a href="http://heathbits.com" target="_blank">heathbits - Freelance Web Designer</a>.&#160; Wow, just wow.&#160; Am I right? &#160;</p><p>Then he tackled the new <a href="http://doodlekit.com" target="_blank">Doodlekit website</a>.&#160; I feel like this is an amazing improvement, not just on the design, but on the content.&#160; That 1, 2, 3 deal on the home page is just Perfect!</p><p>After that he completely redesigned the <a href="http://doodlekit.com/blog/entry/313" target="_blank">Doodlekit Admin Interface</a>. &#160; Moving the menu to the top gives us a lot more horizontal space.&#160; The end result is a much more functional and organized admin tool, plus it matches the main site very well.</p><p>Lastly he re-vamped the <a href="http://doodlebit.com" target="_blank">Doodlebit website</a>.&#160; One of the main goals with this was to separate Doodlekit and Doodlebit a little more.&#160; Doodlekit can remain relatively self sustained, while we get more contracting jobs through Doodlebit.&#160;&#160;</p><p>I can't wait to see the new Doodlekit layouts he's working on.&#160;<br /></p> Thu, 15 Mar 2007 10:29:00 -0500 http://garbageburrito.com/blog/entry/375/doodle-design-frenzy http://garbageburrito.com/blog/entry/375/doodle-design-frenzy Inside DK: Content Management <p>One of the core features I wanted to implement when I started designing <a href="http://doodlekit.com" target="_blank">doodlekit</a> was the Content Management.&nbsp; CMS Software can get very complicated, very quickly.&nbsp; You have approval workflows, publishing, asset management, and security.&nbsp; We were targeting small businesses and families that would probably end up in tears faced with this kind of complexity.&nbsp; So I started thinking, what&#39;s the easiest way to edit a web page?&nbsp; What if you could go to that page, click edit, change something,&nbsp; click save, and see your change right there and then?&nbsp; That&#39;s about as simple as it gets.<br /><br />The concept is simple, you have a link at the bottom of the page that says &quot;edit this page&quot;.&nbsp; When that link is clicked, a form containing an editor is loaded through Ajax and displayed over the content.&nbsp; After the content is modified you click save and the data is saved through another Ajax call.&nbsp; The form is hidden and the content dynamically reloaded, all without a page refresh. &nbsp;<br /><br /><span style="font-weight: bold">Quick Edits</span><br /><br />This idea spawned what would define the majority of doodlekit&#39;s interface, we call it the Quick Edit.&nbsp; These are used in all areas of the site to give users a way to quickly change things without a complicated admin interface. &nbsp;<br /><br />We start out with a two divs, the overlay, and the quick_form.&nbsp; These are hidden by default and displayed when necessary.&nbsp; The overlay div is used to place a semi-transparent veil over the entire page. Some CSS trickery caters to IE6 and it&#39;s lack of alpha PNG support.&nbsp; The quick_form div is centered horizontally using CSS, and vertically using Javascript.&nbsp; This is because the height of the div stretches based on the content loaded.&nbsp; Writing all this down is giving me some ideas on how to improve it; but for now, it aint broke. &nbsp;<br /><br />The actual CSS and javascript is too verbose to include in this post.&nbsp; <a href="/media/download/download_file/28/quick_edit.html" target="_blank">Here is an example html file</a> that I whipped up real quick like.<br /><br />The Ajax link code was getting pretty gnarly, so I moved it into a helper<br /><br /><span style="font-family: courier new,courier">&nbsp; def edit_page_link</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; link_to_remote(&quot;edit this page&quot;, </span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { :update =&gt; &#39;quick_edit_content&#39;, </span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :url =&gt; { :action =&gt; &#39;quick_edit&#39;, :id =&gt; @page.id },</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :complete =&gt; &quot;showQuickForm();addTiny(&#39;page[content]&#39;);afterQuickForm();&quot; }, </span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :class&nbsp; =&gt; &#39;crud_link&#39;, </span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :id =&gt; &#39;quick_edit_link&#39;)</span><br /><span style="font-family: courier new,courier">&nbsp; end</span><br />&nbsp; <br />Pretty basic Rails/Prototype Ajax stuff.&nbsp; You&#39;ll see that after the content is loaded I show the quick form and overlay, load up the TinyMCE editor, and the after callback will center the div vertically.&nbsp; The order is important. <br /><br />The form code was also moved to a helper.<br /><br /><span style="font-family: courier new,courier">&nbsp; def edit_page_form</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; form_remote_tag(:update =&gt; &#39;main_content&#39;, </span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :url =&gt; { :controller =&gt; &#39;pages&#39;, :action =&gt; &#39;quick_save&#39; },</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :before =&gt; &quot;hideQuickForm();removeTiny(&#39;page[content]&#39;);&quot;,</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :complete =&gt; &quot;new Effect.Highlight(&#39;quick_content&#39;);&quot;)</span><br /><span style="font-family: courier new,courier">&nbsp; end</span><br />&nbsp; <br />Again, pretty basic.&nbsp; The quick_save action saves and returns the rendered content.&nbsp; It hides the quick form and overlay and removes the TinyMCE control.&nbsp; After the content is dynamically updated it highlights it to imply that it&#39;s changed.<br />&nbsp; <br /><span style="font-weight: bold">WYSIWYG</span><br /><br />If this tool is going to be used by laymen, then it pretty much has to have a WYSIWYG.&nbsp; I had experience with TinyMCE, and was impressed, so I went with it.&nbsp; Right off the bat I had troubles loading it through Ajax.&nbsp; The way the quick edit works is by loading the form dynamically through Ajax into a hidden div, and then displaying that div.&nbsp; TinyMCE assumes that the form is already on the page.&nbsp; The fix was easy, but not easy to find.<br /><br />I setup the editor with the normal tinyMCE.init( method, however the &#39;mode&#39; should be set to...<br /><br /><span style="font-family: courier new,courier">&nbsp; mode : &quot;specific_textareas&quot;,</span><br />&nbsp;&nbsp; &nbsp;<br />This way it doesn&#39;t try to automatically apply the editor to all textareas on the page.&nbsp; Then I added the following javascript method. This is called after the Ajax load is complete. Don&#39;t ask me to explain it. &nbsp;<br /><br /><span style="font-family: courier new,courier">&nbsp; function addTiny(el) {</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; if (window.tinyMCE) {</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tinyMCE.idCounter=0;</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tinyMCE.execCommand(&quot;mceAddControl&quot;,false,el);</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tinyMCE.execInstanceCommand(el, &#39;mceFocus&#39;);</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; }</span><br /><span style="font-family: courier new,courier">&nbsp; }</span><br />&nbsp; <br />That looked good, but when I hit save, it didn&#39;t work.&nbsp; This is my theory on what&#39;s happening (yes I&#39;m too lazy to verify it).&nbsp; TinyMCE uses hooks in the form to transfer the data from the editor to the actual textarea when the form is submitted.&nbsp; I&#39;m assuming that Prototypes Ajax form submission somehow bypasses these hooks.&nbsp; So I just have to manually call this callback.<br />&nbsp;<br /><span style="font-family: courier new,courier">&nbsp; function saveTiny() {</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; if (window.tinyMCE) {</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tinyMCE.triggerSave();</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; }</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; return true;</span><br /><span style="font-family: courier new,courier">&nbsp; }</span><br />&nbsp;<br />Then the submit button...<br /><br /><span style="font-family: courier new,courier">&nbsp; &lt;%= submit_tag &#39;Save&#39;, :onclick =&gt; &quot;return saveTiny();&quot; %&gt;</span><br />&nbsp; <br />I also wrote a <a href="http://garbageburrito.com/blog/entry/89" target="_blank">GZip Compressor for TinyMCE</a>.<br /><br /><span style="font-weight: bold">URLs</span><br /><br />I&#39;m a stickler for pretty URLs, especially if it&#39;s a link that people are likely to send to others.&nbsp; I wanted each page to have a nice URL, instead of ?page=1. &nbsp;<br /><br />Each page record in the database has a &#39;handle&#39;, which is dynamically generated from the user provided page title.&nbsp; If same page title already exists, then an incrementing number is placed at the end.&nbsp; Every site has a Home page that cannot be deleted.&nbsp; It&#39;s handle is automatically set to &#39;index&#39;.<br /><br />Here&#39;s how it works<br /><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; @page.handle = @page.page_name.gsub(/[^\w|\s]+/, &quot;&quot;).gsub(/\s+/, &quot;_&quot;).downcase</span><br /><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; count = 1;</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; while Page.find_by_handle(@page.handle, @site)</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @page.handle = name + count.to_s</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count += 1</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; end</span><br />&nbsp;&nbsp; &nbsp;<br />I take the page name, strip anything that&#39;s not a word character or space, change spaces to underscores, and make it lowercase.&nbsp; I start searching for another page with the same handle, and add a number to the end if one is found.<br /><br />The routing for this is very simple.&nbsp; I just add this to my routes.rb file.<br /><br /><span style="font-family: courier new,courier">&nbsp; map.connect &#39;/home/:id&#39;, :controller =&gt; &quot;pages&quot;</span><br /><br />Now I can go to http://garbageburrito.com/home/about, and it will forward me to the pages controller with the id parameter set to &#39;about&#39;.<br /><br />The action in the pages controller looks like this.<br /><br /><span style="font-family: courier new,courier">&nbsp; def show</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; @handle = params[:id] || &quot;index&quot;</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; @page = Page.find_by_handle(@handle, @site)</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; if @page.private</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; check_login</span><br /><span style="font-family: courier new,courier">&nbsp;&nbsp;&nbsp; end</span><br /><span style="font-family: courier new,courier">&nbsp; end</span><br />&nbsp; <br />If there&#39;s no handle provided then default to index.&nbsp; Then just look up the page by the handle and check do a security check.&nbsp; Notice that the site object is passed to the query to ensure you can only pull pages within this site&#39;s scope.&nbsp; For more on that see <a href="http://garbageburrito.com/blog/entry/179" target="_blank">Inside DK: Multi-site Rails Applications.</a> </p><p>I would have liked to cover asset management and the menu system, however I think trying to squeeze those into this article would be too much.<br /><br />Doodlekit&#39;s content management is so fluid and simple I don&#39;t even like to call it CMS, I prefer dynamic pages.&nbsp; This is the core concept that led the other features such as the blog and forums to fruition.&nbsp; I can only hope that we can constrain new features to be this elegant.</p> Thu, 22 Feb 2007 11:40:00 -0600 http://garbageburrito.com/blog/entry/331/inside-dk-content-management http://garbageburrito.com/blog/entry/331/inside-dk-content-management Coders For Obama: Fundraiser <img src="/media/image/image/3647/main/codersforobama.png" alt=" " width="150" height="57" align="right" />When Barack Obama was elected to the Senate in 04, something about him struck me.&nbsp; I&#39;m not sure what it was, but I said to myself &quot;This man will be the President someday&quot;.&nbsp; I never expected that it would happen so soon, but here we are.<br /><br />For those of you that are not yet familiar with Barack, I suggest you watch some of his speeches on YouTube.&nbsp; Particularly his 04 DNC Keynote, and his candidacy announcement. What I love most about Barack is that he says what we&#39;re all thinking.&nbsp; His honesty comes through in a way I&#39;ve never seen in a politician.&nbsp; But don&#39;t let me convince you, see for yourself.<br /><br />His campaign is not accepting donations from lobbyists or political committees, it&#39;s relying on grass roots campaigns like this.&nbsp; They have a realy nice website, complete with social networking to build small communities of real people to help the cause.<br /><br />So my idea was to start a network of developers with blogs that will accept donations to the Obama for America campaign on their behalf.&nbsp; It&#39;s simple, you put a link in the sidebar of your blog to a fundraiser page.&nbsp; If someone enjoyed or got value from your post, then they can make a donation for you.&nbsp; It can be $1, $10, anything will help.<br /><br />The current goal is $1000, which I&#39;m hoping we can reach very quickly. &nbsp;<br /><br />You can use the following HTML block, or create your own image and link. &nbsp;<br /><br /><blockquote><font face="courier new,courier">&lt;a href=&quot;http://my.barackobama.com/page/outreach/view/main/codersforobama&quot;&gt;&lt;img src=&quot;http://garbageburrito.com/media/image/image/3647/main/codersforobama.png&quot; border=&quot;0&quot;&gt;&lt;br&gt;<br />Enjoyed my blog?&nbsp; Make a donation in my name.&lt;/a&gt;</font><br /></blockquote><br />So rise up friends.&nbsp; Doesn&#39;t matter what camp you come from, we can all be in this as Coders.&nbsp; Let me know if you&#39;re participating, and I&#39;ll create a page with links to all the blogs.<br /><br /> Mon, 12 Feb 2007 15:36:00 -0600 http://garbageburrito.com/blog/entry/311/coders-for-obama-fundraiser http://garbageburrito.com/blog/entry/311/coders-for-obama-fundraiser Quote of the Day <p>My good friend <a href="http://6brand.com">danger</a> (who is awesome cause he tolerates my danger mouse jokes) was just <a href="http://railsforum.com/viewtopic.php?pid=11186#p11186">promoted to moderator status</a> on railsforum.&nbsp; He left this in response to his congratulatory thread.</p><blockquote><p>&quot;<em>May your work be speedy, may your problems be short-lived, and may your code look good ;-)</em> &quot; - Jack Danger Canty</p></blockquote><p>I&#39;d say that pretty well embodies the spirit of a rails developer.<br /></p> Thu, 25 Jan 2007 10:22:00 -0600 http://garbageburrito.com/blog/entry/292/quote-of-the-day http://garbageburrito.com/blog/entry/292/quote-of-the-day