<?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-5459321208870016810</id><updated>2011-10-19T11:06:00.399-07:00</updated><category term='ruby'/><category term='uniqueness'/><category term='unique'/><category term='SEO Optimization'/><category term='refactoring'/><category term='sass'/><category term='software'/><category term='bugs'/><category term='DRY'/><category term='architect'/><category term='buildbot'/><category term='CI'/><category term='selenium'/><category term='testing'/><category term='hg'/><category term='compass'/><category term='mercurial'/><category term='continuousintegration'/><category term='rubyonrails'/><category term='open-source'/><category term='URLs'/><category term='firstpost'/><title type='text'>acts_as_architect</title><subtitle type='html'>A software architect details his experiences using Ruby on Rails to create the website for Caring.com</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-8266351510728656712</id><published>2009-09-20T00:26:00.000-07:00</published><updated>2009-09-20T00:30:22.007-07:00</updated><title type='text'>This blog has moved.</title><content type='html'>I can't bear to use blogger anymore. I've moved my blog to github. If you're a subscriber, please visit my &lt;a href="http://chriseppstein.github.com/"&gt;new blog&lt;/a&gt; and subscribe to it. Also, please read my first post there on &lt;a href="http://chriseppstein.github.com/blog/2009/09/20/why-stylesheet-abstraction-matters/"&gt;why abstraction matters in stylesheets&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-8266351510728656712?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/8266351510728656712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=8266351510728656712' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/8266351510728656712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/8266351510728656712'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2009/09/this-blog-has-moved.html' title='This blog has moved.'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-5018554649922385139</id><published>2009-04-26T11:59:00.000-07:00</published><updated>2009-04-26T12:07:49.725-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open-source'/><category scheme='http://www.blogger.com/atom/ns#' term='sass'/><category scheme='http://www.blogger.com/atom/ns#' term='compass'/><title type='text'>Compass Screencast</title><content type='html'>I've put together a screencast that demonstrates how to get up and running with compass and use it to style a basic webpage. It covers some basic sass syntax in case you're not familiar with it, and then shows how you can create a stylesheet for your semantic markup. If you've been skeptical of the value that compass and sass provide, hopefully seeing it in action will convince you!

&lt;br/&gt;
&lt;br/&gt;
&lt;object width="700" height="394"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=4335944&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=0&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=4335944&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=0&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="700" height="394"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-5018554649922385139?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/5018554649922385139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=5018554649922385139' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/5018554649922385139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/5018554649922385139'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2009/04/compass-screencast.html' title='Compass Screencast'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-3954900313642736644</id><published>2009-04-20T11:05:00.000-07:00</published><updated>2009-04-20T11:40:56.104-07:00</updated><title type='text'>The Open Source Project that Social Media Made</title><content type='html'>I realized recently that my open source project, &lt;a href="http://compass-style.org/"&gt;Compass&lt;/a&gt;, has become popular enough to consider it moderately successful. It has a community, I get bug reports and patches. People write &lt;a href="http://awardwinningfjords.com/?p=3"&gt;blog&lt;/a&gt; &lt;a href="http://www.tenthline.com/blog/2009/03/13/compass-css-doesnt-have-to-suck/"&gt;posts&lt;/a&gt; &lt;a href="http://log.openmonkey.com/post/78482055"&gt;about&lt;/a&gt; it without prompting. I've been asked to give &lt;a href="http://pivotallabs.com/talks"&gt;talks about it&lt;/a&gt;. All those benefits that you tell your company they will get when you release something into the wild, but that rarely actually happens.  It occurred to me that the history of how Compass came to be is actually the story of how social media can make and shape a software project. So I thought I'd share that history with you.

&lt;h4&gt;Github&lt;/h4&gt;My friend &lt;a href="http://github.com/dustin"&gt;Dustin&lt;/a&gt; turned me onto &lt;a href="http://github.com/"&gt;github&lt;/a&gt; when it first launched. Suddenly the bar to release open source software had lowered to the point where it was easy enough to share code without putting much effort into project hosting. In those &lt;a href="https://github.com/chriseppstein/freebase/tree"&gt;first&lt;/a&gt; &lt;a href="https://github.com/chriseppstein/lame_stats/tree"&gt;few&lt;/a&gt; &lt;a href="https://github.com/chriseppstein/pywebmvc/tree"&gt;months&lt;/a&gt; I &lt;a href="http://github.com/caring/acts_as_url_param/tree"&gt;threw&lt;/a&gt; &lt;a href="http://github.com/caring/composition_generator/tree"&gt;almost&lt;/a&gt; a &lt;a href="http://github.com/caring/default_routing/tree"&gt;dozen&lt;/a&gt; &lt;a href="http://github.com/caring/gibberish_db/tree"&gt;projects&lt;/a&gt; &lt;a href="http://github.com/caring/with_url_scope/tree"&gt;up&lt;/a&gt;. Finally, I could give back to the community almost as easily as I could take. It was addictive. Every new watcher was a glorious moment. One day, after porting the blueprint css framework to sass, I decided to throw that up there too. It was only about 8 hours of work, maybe less. I wasn't nearly as proud of it as some of the other projects I released. But I thought I might be able to save someone from having to do their own port. This project was called "&lt;a href="http://github.com/chriseppstein/blueprint-sass/tree/master"&gt;blueprint-sass&lt;/a&gt;" and I still keep it around as "Github SEO Strategy." Within a couple weeks, this project had attracted more watchers than all my others combined. I knew I had struck a nerve but I wasn't really sure how to proceed.

&lt;h4&gt;Blueprint&lt;/h4&gt;So I took my case to the age-old social media forum that is the Newsgroup. I suggested to the blueprint mailing list, that my new stylesheets built in sass should be the blueprint core stylesheets and that what they generated could be the blueprint distribution. And that those who wanted to use the sass stylesheets directly, certainly could. This seemed (and continues to seem) like the best way to manage that code base, since maintaining a port to a different language is a lot of extra work. I got a green light to begin work on a &lt;a href="http://github.com/chriseppstein/blueprint-css/commits/sass"&gt;blueprint proof of concept based on sass&lt;/a&gt; which I threw myself into with full gusto. Many of the blueprint tools became obsolete because the sass stylesheets could perform programmatic calculations and sass has several output modes. Before I knew it, except for the generated css that came out, this was a whole new project. The scope of the changes ruffled a few feathers, I guess, and so it was &lt;a href="http://groups.google.com/group/blueprintcss/msg/e5ce914c5aa9df0b?hl=en"&gt;decided that my code wouldn't be merged&lt;/a&gt;. As sad I was about this outcome, I was very proud of what I had built and the quality of my code was certainly a reflection of the expectation of becoming part of one of the &lt;a href="http://github.com/popular/watched"&gt;most watched projects on github&lt;/a&gt;. I was not to be deterred in my efforts by this set back. In retrospect, it was probably one of the best things that could have happened.

&lt;h4&gt;Heading Off in a New Direction&lt;/h4&gt;While coding my port of blueprint, it occurred to me that so much of what I was building had little to nothing "blueprint specific" about it. The tool set was basically generic, change the Sass stylesheets and you could have a new sass-based framework. Now anyone who knows me can tell you that I love meta-ness. And since programming and software archicture is what I do best -- not actually devising clever css frameworks -- I thought I should stick to what I know and provide a tool set to css frameworks and the users of those frameworks everywhere so that they could take advantage of the real star of the show: &lt;a href="http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html"&gt;Sass&lt;/a&gt;. Someone needed to point them all in a new direction: &lt;a href="http://wiki.github.com/chriseppstein/compass/patterns"&gt;A new way of creating and sharing design from the geniuses of the web design world to the mere mortal web developers&lt;/a&gt; (like myself) who are much better working on someone else's design than coming up with our own. And so the two-fold vision of providing a compelling library to promote the use of Sass and to bring to web design the concept of sharing code was born. I gave it the name: Compass. Even today my vision for what compass should be is not yet fully realized. But I'm down to my last few unimplemented features before I can declare it version 1.0.
&lt;h4&gt;Guerrilla Marketing via Social Media&lt;/h4&gt;A wise man once said, "Don't worry about people stealing your ideas. If they are good enough, you'll have to cram it down their throats." (I wish I knew who to attribute that to, I think I heard it in a TED talk). As I was building Compass, I knew from my experience with blueprint, that if my idea was actually a good one, it was one of those requiring cramming. So I kept an eye out for social media opportunities to market it. If an article on CSS or especially CSS frameworks showed up on reddit, digg or ycombinator news, I found a way to link to my project in there. I left comments on relevant blog posts. I eavesdropped on twitter for people talking about blueprint and css, and pointed them toward compass. Week by week, compass grew. Compass-style.org got "stumble-upon"-ed. A small but fervent community of early adopters saw what compass and sass could do for web development, and they latched on to it. They blogged about it, and they tweeted. Oh my did they ever tweet. It's just so damn easy to send out a link to a new and exciting technology and thanks to the large number of ruby developers on twitter who are always eager to try new things, compass spread quickly.

&lt;h4&gt;Collecting Feedback via the Twitterverse&lt;/h4&gt;Twitter was instrumental in my ability to communicate directly with my users or potential users and learn from them. I learned from their reservations, and I learned from their criticisms. Their positive feedback gave me the energy to keep going. I was able to find out about and fix bugs within hours instead of days. My tech support responses came back to folks asking the twitter-void for compass help. Before twitter, those might have been users who gave up before completing their initial evaluation.

&lt;h4&gt;My Commitment to an Open Project&lt;/h4&gt;An open project means much more to me than just being open source. An open project is one that is conducted transparently. Compass is here in the form that it is, because it is what is needed. It's solving real problems for folks today. I know this because they told me through their actions on social media sites like Github and Twitter. But Compass can be much, much more than it is right now. Compass and Sass can change the way we think about the implementation of website design. I have a vision of a future, but I want to work with all of you to make sure it's the right one. I'm listening to your feedback and thoughts and acting on them. I know that a large part of the success of Compass to date has depended on that conversation, and I intend it to continue. Compass is nothing in the long run without its community and I look forward to fostering it more in the coming years.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-3954900313642736644?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/3954900313642736644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=3954900313642736644' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/3954900313642736644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/3954900313642736644'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2009/04/open-source-project-that-social-media.html' title='The Open Source Project that Social Media Made'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-6965026840765363087</id><published>2009-04-14T21:10:00.000-07:00</published><updated>2009-04-14T21:25:42.325-07:00</updated><title type='text'>The Names You Give to Your Data Models Matters More Than You Might Think</title><content type='html'>&lt;p&gt;Early in the days of &lt;a href="http://www.caring.com/"&gt;Caring.com&lt;/a&gt; we planned to build a personal homepage for our users that would delight them with a customized selection of content that was appropriate for the issues they were dealing with. Many of our early architectural decisions centered around this premise. Unfortunately, the breadth and depth of our content was not sufficient to support that feature and it was shelved rather than disappoint our users. Almost two years later, we have an &lt;a href="http://www.caring.com/site-map"&gt;amazing selection of content for the most common care giving issues&lt;/a&gt;, a &lt;a href="http://www.caring.com/experts"&gt;small army of experts waiting to answer care giving questions&lt;/a&gt; from our users, a small but &lt;a href="http://www.caring.com/community"&gt;active community of caregivers&lt;/a&gt; that are helping each other get through some of the most difficult times of their lives, and a &lt;a href="http://www.caring.com/local"&gt;comprehensive guide to care giving resources in your area&lt;/a&gt;. We are finally ready to personalize our site and deliver what we hope will be delightful experience for those people who choose to fill out a short questionnaire (not yet launched at the time of this writing).
&lt;p&gt;So I got to take some old code off the shelf and dust it off. It was exciting, and a little frightening. In those early days, I knew I needed a model to represent the person or people that our user is caring for. With my overly analytical, highly trained engineer brain and no formal education in geriatrics or care giving, I called this model the &lt;span style="font-family:courier new;"&gt;CareTarget&lt;/span&gt;. That name stuck, and from time to time, I'd hear myself and others refer to the care target when we needed a term to use in conversation. And while this name clearly and unambiguously describes the purpose of the model, &lt;span style="font-weight: bold;"&gt;it was the wrong term&lt;/span&gt;. These are people, not targets. They are your loved ones and they are my parents. I felt the term &lt;span style="font-family:courier new;"&gt;CareTarget&lt;/span&gt; was dehumanizing. These are the people who receive our care, because we love them and because they deserve it. They are a &lt;span style="font-family:courier new;"&gt;CareRecipient&lt;/span&gt;. Sitting alone in my office, with deadlines to hit, I decided that I could not allow this term to persist. In two weeks the number of uses of these names, would double or more -- there was no better time to rename them than right then, and so it was done right then. Similarly, a model called CareTargetType was changed to CareRelationship (E.g. Mother, Father).
&lt;p&gt;When I came to scrum (our daily standup meeting) the next day and announced I had made no progress on my tasks, not everyone understood why the change was so imperative. But these names are at the very heart of what Caring.com is and does, nothing that central can be allowed to be wrong. Not even their names. In total, I spent a day and a half renaming what were arguably fine names -- they needn't have ever been exposed to our users, but now I never have to worry that they might. And in a few months, when I hear folks in the halls of Caring.com talking about the care relationship one of our users has to their care recipient, I'm going to smile because I understand that the names in our code have a real and lasting effect on the tone and understanding of our domain. Software Architecture and Design is most certainly a technical field, but that doesn't mean it has to be dry and impersonal.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-6965026840765363087?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/6965026840765363087/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=6965026840765363087' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/6965026840765363087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/6965026840765363087'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2009/04/names-you-give-to-your-data-models.html' title='The Names You Give to Your Data Models Matters More Than You Might Think'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-272819568291996287</id><published>2009-02-15T00:31:00.000-08:00</published><updated>2009-02-15T10:05:14.944-08:00</updated><title type='text'>Disadvantages of CSS Frameworks: A Sass-y Response</title><content type='html'>A &lt;a href="http://www.smashingmagazine.com/2007/09/21/css-frameworks-css-reset-design-from-scratch/"&gt;recent post&lt;/a&gt; from &lt;a href="http://www.smashingmagazine.com/"&gt;Smashing Magazine&lt;/a&gt; made a very thoughtful and balanced analysis of CSS Frameworks. I recognized the drawbacks of using CSS frameworks long ago and set out to keep the good parts and eliminate the bad parts, as much as is possible, by using Sass for the core technology behind Compass.

&lt;p&gt;Let’s be honest, CSS frameworks, while meeting the technical definition of a “framework”, are not anything more than a collection of css classes that you can use in your HTML. They are not a framework for writing your stylesheets -- they are a framework for building webpages, and this is the fundamental difference when you use Compass &amp;amp; Sass.&lt;/p&gt;
So here’s the list of disadvantages of CSS frameworks from Smashing Magazine, and my response to each with respect to Sass and Compass.


&lt;h3 id="you_need_time_to_fully_understand_the_framework"&gt;You need time to fully understand the framework.
&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;Using external CSS frameworks, you still need a profound understanding of your code. You need to know exactly how your framework is actually built. “By building a site from the ground up, you gain a knowledge of your site’s architecture that can’t be learned through any study or documentation.” [&lt;a href="http://warpspire.com/features/css-frameworks/"&gt;Why I don’t use frameworks&lt;/a&gt;]&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Yes, you’ll need to know your how your framework works. For example, you’ll need to know that a grid is built using floats and how changes to the element’s box attributes (padding, margins, border) will affect the layout. Your framework should document this, and if it doesn’t you should go read the code and understand it.&lt;/p&gt;&lt;h3 id="you_might_inherit_someones_bugs_or_mistakes"&gt;You might inherit someone’s bugs or mistakes.&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;If you use external CSS Frameworks you might get in trouble fixing someone else’s bugs which is far more time-consuming than fixing your own bugs.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Absolutely. This is of course not unique to CSS frameworks, this is a risk you take any time you stand on the shoulders of someone else. Fixing someone else’s bug is slower than fixing your own. But let’s think about this. How many bugs will you make in your attempt to reproduce what the framework gives you? I’m guessing more. But it’s quite likely that you &lt;strong&gt;won’t&lt;/strong&gt; encounter someone else’s bug. But let’s pretend that you do; what is so bad about this? If there’s a community of people sharing a common code-base, once one person finds that bug it’s fixed for everyone. Thanks! You’re a great team player who has been benefiting from the help of others and you’ve just contributed back a small portion of your time savings. As with any build vs. buy decision, you need to consider how widely used the project is, how engaged the maintainer is, etc.&lt;/p&gt;&lt;p&gt;That said, the cost of upgrading a traditional CSS framework is high. The installation is manual and unless you were careful to never make a change to the files provided by your CSS framework, you’ll be stuck trying to manually merge your changes. Worse, some CSS frameworks, in an attempt to make them more customizable, will generate CSS just for you that you can then put into your project. Upgrading will mean regenerating this CSS using the same inputs.&lt;/p&gt;&lt;p&gt;Compass was designed to be upgraded trivially addresses this problem in two ways. It relies on the ruby installation framework called rubygems so upgrading is a simple matter of issuing:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo gem update chriseppstein-compass
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, because you’re code is written in Sass instead of CSS, you simply need to recompile your stylesheets and you’re up and running with the latest bug fixes:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ compass --update myproject
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="you_develop_sites_upon_a_framework_not_upon_the_solid_knowledge_of_css"&gt;You develop sites upon a framework, not upon the solid knowledge of CSS.&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;“A big problem with frameworks is when up and coming developers attach themselves to a framework as opposed to the underlying code itself. The knowledge gained in this case surrounds a specific framework, which severely limits the developer.” [&lt;a href="http://mondaybynoon.com/2007/08/27/please-do-not-use-css-frameworks/"&gt;Please Do Not Use CSS Frameworks&lt;/a&gt;, by Jonathan Christopher]&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This argument has been made every time a technology evolves a new layer of abstraction. There’s a certain amount of truth to this argument if you’re speaking about complex code frameworks like rails, MFC, etc. But in the case of CSS frameworks and Compass, I just don’t agree. As we said above you’re going to need to take the time to fully understand the framework. The output and construction of the framework is not hidden from you and you’re fundamentally working with abstractions that you have in CSS anyway.&lt;/p&gt;&lt;h3 id="you_get_a_bloated_source_code"&gt;You get a bloated source code.&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;“Whether it be in a server side language framework or JavaScript library, there is often a large percentage of code that will never be executed. While not a major issue server side, this can greatly degrade the performance of a client side framework such as a JavaScript library.” [&lt;a href="http://mondaybynoon.com/2007/08/27/please-do-not-use-css-frameworks/"&gt;Please Do Not Use CSS Frameworks&lt;/a&gt;, by Jonathan Christopher]&lt;/p&gt;&lt;/blockquote&gt;This is true. You &lt;em&gt;can&lt;/em&gt; degrade the performance of a browser. But did you? I’ve seen this argument before, but I’ve never seen anyone back it up with numbers. But let’s make an aesthetic argument instead: &lt;span style="font-weight: bold;"&gt;Having a bunch of CSS that is superfluous to your project is ugly&lt;/span&gt;. I agree. Compass currently has &lt;strong style="font-weight: normal;"&gt;4 CSS frameworks&lt;/strong&gt; that you can use. This ridiculous bloat just doesn’t matter though because you control what ends up in your stylesheets. You can select the non-semantic versions of these frameworks with simple commands like:

&lt;pre&gt;&lt;code&gt;@import compass/reset.sass, @import compass/reset.sass, blueprint.sass

+blueprint
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or you can be more selective. For example, if you just want blueprint’s grid system:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;@import compass/reset.sass, blueprint.sass

+blueprint-grid
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="css_can_not_be_framed_semantically"&gt;CSS can not be framed semantically.&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;“CSS and (X)HTML go hand in hand. (X)HTML is a language semantic in nature, which is impossible to wrap up in the style of a framework. Each and every project is unique in and of itself, right down to the document structure, classes, and ids. A CSS framework passively removes a great majority of semantic value from the markup of a document and, in my opinion, should be avoided.” [&lt;a href="http://mondaybynoon.com/2007/08/27/please-do-not-use-css-frameworks/"&gt;Please Do Not Use CSS Frameworks&lt;/a&gt;, by Jonathan Christopher]&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Bingo. This is, hands down, my #1 problem with CSS Frameworks. CSS based frameworks have to work within the limitations of the technology. As such, you have to make sure your markup conforms to the framework and violate the best practice of keeping your content and presentation separate. And this is the #1 reason Compass exists. I didn’t want to choose between using a framework and following best practices. I wanted to eat my cake too! Compass, together with the awesome concept in Sass called a “Mixin”, allows you to build semantic stylesheets!&lt;/p&gt;&lt;pre&gt;&lt;code&gt;@import blueprint.sass
#page
+container
#sidebar
+column(8)
#content
+column(16, true)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This generates the following CSS:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#page { width: 950px; margin: 0 auto; overflow: auto;
   overflow: -moz-scrollbars-none; display: inline-block; }
#page { display: block; }
#page #sidebar { float: left; width: 310px; margin-right: 10px; }
#page #content { float: left; width: 630px; margin-right: 0; }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, your &lt;code&gt;#page&lt;/code&gt; element is a container that has been clear-fixed and set to the appropriate width. The &lt;code&gt;#sidebar&lt;/code&gt; is an 8 unit grid column and your #content is a 16 unit grid column with no right margin because the “true” means it is the last column in a row. This is a simple example, but I hope it demonstrates how you can start to think about your stylesheets in a new way and why I say Compass is a real stylesheet framework, not just a collection of classes.&lt;/p&gt;&lt;h3 id="ignoring_the_uniqueness_of_your_projects"&gt;Ignoring the uniqueness of your projects.&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;Designs should be based upon the content, not upon a standard template you use over and over again.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I don’t know. I’ve never had a framework dictate my design. &lt;a href="http://www.dsrstudio.com/"&gt;Our designer&lt;/a&gt; would never stand for that. Sometimes the framework informs some basic guidelines surrounding widths, etc. If your framework is dictating a design you don’t want, pick a different one. Compass comes with three (&lt;a href="http://wiki.github.com/chriseppstein/compass/blueprint-documentation"&gt;blueprint&lt;/a&gt;, &lt;a href="http://wiki.github.com/chriseppstein/compass/yui-documentation"&gt;yui&lt;/a&gt;, and &lt;a href="http://wiki.github.com/chriseppstein/compass/compass-core-documentation"&gt;compass-core&lt;/a&gt;) and you can install a plugin to get support for 960.gs. Even if you don’t want grids, these frameworks can help you set up your typography and vertical rhythm and perform common styling tasks like making lists lay out horizontally or building a tag cloud. Here’s &lt;a href="http://www.eppsteins.net/compass/examples/compass/utilities.html"&gt;a demo of some of the compass utilities&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;In conclusion&lt;/h3&gt;Using Sass instead of CSS let's you insulate yourself from the nitty gritty mechanics of CSS while keeping you close to the syntax and semantics of CSS. It provides the power to keep your stylesheets both DRY and semantic and let's you stand on the shoulders of others or even your yourself (reuse across projects, websites). Compass is the pointing they way to cleaner, more maintainable stylesheets. If you want to use it, I hope you get value out of it. But even if you don't, just sticking with Sass and building up your own framework will give you many of the benefits I've discussed here.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-272819568291996287?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/272819568291996287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=272819568291996287' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/272819568291996287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/272819568291996287'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2009/02/disadvantages-of-css-frameworks-sass-y_15.html' title='Disadvantages of CSS Frameworks: A Sass-y Response'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-4311810615876790419</id><published>2009-01-15T10:51:00.000-08:00</published><updated>2009-01-15T10:59:06.377-08:00</updated><title type='text'>I'm on the Sass core team</title><content type='html'>While it actually happened back in late September, &lt;a href="http://nex-3.com/posts/78-chris-eppstein-joins-sass-team"&gt;Nathan recently announced that I've joined the Sass Core Team&lt;/a&gt;. Sass is such an amazing technology! It has dramatically improved our productivity at &lt;a href="http://www.caring.com/"&gt;Caring.com&lt;/a&gt;. and I'm happy to be part of the team and helping drive its development. I do all my work on &lt;a href="http://github.com/chriseppstein/haml"&gt;my fork of haml&lt;/a&gt; so if you want to keep tabs on upcoming changes to sass, you should watch it on github.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-4311810615876790419?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/4311810615876790419/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=4311810615876790419' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/4311810615876790419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/4311810615876790419'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2009/01/im-on-sass-core-team.html' title='I&apos;m on the Sass core team'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-2587769818814321270</id><published>2008-11-18T14:36:00.000-08:00</published><updated>2008-11-18T14:38:52.615-08:00</updated><title type='text'>Creating a Reusable Three-column Layout using Compass</title><content type='html'>Let's pretend we want to build a three column layout like so:
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_SN7npPwoqNg/SQzCgJSMmJI/AAAAAAAAACM/y7WN8M0-5K0/s1600-h/Three+Column.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 326px; height: 400px;" src="http://3.bp.blogspot.com/_SN7npPwoqNg/SQzCgJSMmJI/AAAAAAAAACM/y7WN8M0-5K0/s400/Three+Column.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5263795921993111698" /&gt;&lt;/a&gt;

Using blueprint, the page is divided into 24 columns. In just a few minutes we whip out our three column layout for our webpage:

&lt;script src="http://gist.github.com/21575.js"&gt;&lt;/script&gt;

And here's our stylesheet that implements this layout:

&lt;script src="http://gist.github.com/21576.js"&gt;&lt;/script&gt;
Again, this is as easy to use as blueprint, but we've avoided making the mistake of putting presentation in our content.

Now let's say you have several different templates and you've been good about making nice semantic names for the different pages and none of the IDs are the same. But we want all of these pages to use the same layout. This is where your power to create abstraction comes in.

Using Sass you can define your own mixins that you can reuse! So you start by defining a three column mixin:

&lt;script src="http://gist.github.com/21577.js"&gt;&lt;/script&gt;

Now you can now simplify your layout declaration even further:
&lt;script src="http://gist.github.com/21578.js"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-2587769818814321270?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/2587769818814321270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=2587769818814321270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/2587769818814321270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/2587769818814321270'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2008/11/creating-three-column-layout-using.html' title='Creating a Reusable Three-column Layout using Compass'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SN7npPwoqNg/SQzCgJSMmJI/AAAAAAAAACM/y7WN8M0-5K0/s72-c/Three+Column.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-1409529812666644458</id><published>2008-11-18T11:46:00.000-08:00</published><updated>2010-05-17T06:42:58.588-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='compass'/><title type='text'>A Compass Primer</title><content type='html'>&lt;p&gt;The goal of this post will be to help you install Compass and Sass and then learn the basics. When you are done, if you follow along, you will have a working stand-alone compass project. If you're looking to better understand what Compass can do for you, please read the &lt;a href="http://acts-as-architect.blogspot.com/2008/11/introducing-compass.html"&gt;compass introduction&lt;/a&gt; and then come back. If you plan to use Compass with Rails, Merb, or StaticMatic, there's specific installation instructions for each available on the &lt;a href="http://github.com/chriseppstein/compass/wikis"&gt;wiki&lt;/a&gt; but for now, you can follow along without them.&lt;/p&gt;
&lt;h3&gt;Some Basic Prerequisites&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Both Compass and Sass require you to have Ruby installed. If you don't have ruby installed, &lt;a href="http://www.google.com/search?q=install+ruby"&gt;please do so now&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Please make sure you have rubygems version 1.3 or greater by running &lt;code&gt;gem -v&lt;/code&gt;.
&lt;/li&gt;&lt;li&gt;Using compass will require you to do some basic command line work. If you're not comfortable using the command line, you should probably pass on Compass for a while until we get more users and some IDE support, etc.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Installing Compass&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;$ gem sources --add http://gems.github.com/
$ sudo gem install chriseppstein-compass
&lt;/code&gt;&lt;/pre&gt;
You have successfully installed compass if you can perform this command:
&lt;pre&gt;&lt;code&gt;$ compass -v
Compass 0.8.15
Copyright (c) 2008 Chris Eppstein
Released under the MIT License.
&lt;/code&gt;&lt;/pre&gt;
Sass, which is part of the Haml project, was also installed for you. Sass comes with two command line tools that you should be aware of:
&lt;ul&gt;&lt;li&gt;&lt;code&gt;sass&lt;/code&gt; - A sass compiler for single files and directories that emits css.&lt;/li&gt;&lt;li&gt;&lt;code&gt;sass-convert&lt;/code&gt; - A sass translator for your existing css, less, sass, and scss files.&lt;/li&gt;&lt;/ul&gt;Both of these tools also operate on "stdin" for easy integration with other command line tools and GUIs.

&lt;h3&gt;Setting up your First Compass Project&lt;/h3&gt;We will now create a new project named "my_compass_project" based on blueprint. The contents of the project will be placed into the my_compass_project subdirectory of your current directory -- which will be created for you.

&lt;pre&gt;&lt;code&gt;$ compass create my_compass_project --using blueprint --syntax sass
  directory my_compass_project/
  directory my_compass_project/images/
  directory my_compass_project/src/
  directory my_compass_project/src/partials/
  directory my_compass_project/stylesheets/
     create my_compass_project/config.rb
     create my_compass_project/src/screen.sass
     create my_compass_project/src/partials/_base.sass
     create my_compass_project/src/print.sass
     create my_compass_project/src/ie.sass
     create my_compass_project/images/grid.png
     exists my_compass_project/stylesheets
    compile my_compass_project/src/ie.sass
     create my_compass_project/stylesheets/ie.css
    compile my_compass_project/src/print.sass
     create my_compass_project/stylesheets/print.css
    compile my_compass_project/src/screen.sass
     create my_compass_project/stylesheets/screen.css

  *********************************************************************
  Congratulations! Your compass project has been created.

  You may now add and edit sass stylesheets in the src subdirectory of your project.

  Sass files beginning with an underscore are called partials and won't be
  compiled to CSS, but they can be imported into other sass stylesheets.

  You can configure your project by editing the config.rb configuration file.

  You must compile your sass stylesheets into CSS when they change.
  This can be done in one of the following ways:
    1. To compile on demand:
       compass compile [path/to/project]
    2. To monitor your project for changes and automatically recompile:
       compass watch [path/to/project]

  More Resources:
    * Wiki: http://wiki.github.com/chriseppstein/compass
    * Sass: http://sass-lang.com
    * Community: http://groups.google.com/group/compass-users/


  Please see the blueprint website for documentation on how blueprint works:

      http://blueprintcss.org/

  Docs on the compass port of blueprint can be found on the wiki:

      http://wiki.github.com/chriseppstein/compass/blueprint-documentation

  To get started, edit the screen.sass file and read the comments and code there.

  To import your new stylesheets add the following lines of HTML (or equivalent) to your webpage:
  &amp;lt;head&gt;
    &amp;lt;link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css" /&gt;
    &amp;lt;link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css" /&gt;
    &amp;lt;!--[if lt IE 8]&gt;
        &amp;lt;link href="/stylesheets/ie.css" media="screen, projection" rel="stylesheet" type="text/css" /&gt;
    &amp;lt;![endif]--&gt;
  &amp;lt;/head&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see from the output several new directories have been created, some sass files have been added already and they've even been compiled to css for you. Before we change anything let's verify that our project is working out of the box.&lt;/p&gt;

&lt;h3&gt;Create an HTML file&lt;/h3&gt;
Let's create the following html file named &lt;code&gt;index.html&lt;/code&gt; in the &lt;code&gt;my_compass_project&lt;/code&gt; directory:

&lt;script src="http://gist.github.com/26240.js"&gt;&lt;/script&gt;
&lt;p&gt;Now open &lt;code&gt;index.html&lt;/code&gt; in your browser and you should see a nice looking &lt;code&gt;H1&lt;/code&gt; that is left justified within the centered 950px blueprint grid container.&lt;/p&gt;

&lt;h3&gt;A Quick Intro to Sass&lt;/h3&gt;
&lt;p&gt;As mentioned already, Compass is built on top of the &lt;a href="http://sass-lang.com"&gt;Sass stylesheet language&lt;/a&gt;. Before we can style our webpage, we should review some Sass basics. Sass provides a much cleaner syntax than CSS for styling your webpages. But before we style our ours, let's review some of the basics of the Sass language. First and foremost, Sass allows you to use indentation to indicate a descendent selector, yielding style&lt;/p&gt;

&lt;script src="http://gist.github.com/26245.js"&gt;&lt;/script&gt;
Which generates two css rules that look like:

&lt;script src="http://gist.github.com/26246.js"&gt;&lt;/script&gt;
This ability to scope selectors allows you to easily avoid the dreaded "CSS Spaghetti" where your styles and class names end up bleeding into areas where you'd rather they didn't.

Have you ever wanted to name your colors so that you wouldn't have to remember the hex values or so that you could change them easily? Sass provides variables to which you can assign values and reuse them over and over again:
&lt;script src="http://gist.github.com/26248.js"&gt;&lt;/script&gt;
Do you find recurring patterns for styles and copy and pasting them into different selectors? Sass provides a concept called a mixin that addresses this problem. You can define a mixin like so:

&lt;script src="http://gist.github.com/26250.js"&gt;&lt;/script&gt;
And once you'd defined your mixin, you can "mix it" into any selector like so:

&lt;script src="http://gist.github.com/26253.js"&gt;&lt;/script&gt;
Mixins can also take arguments, define rules for descendants, and even include other mixins:

&lt;script src="http://gist.github.com/26254.js"&gt;&lt;/script&gt;
&lt;code&gt;+clearfix&lt;/code&gt; is a mixin provided by the &lt;a href="http://compass-style.org/docs/reference/compass/utilities/"&gt;compass utilities module&lt;/a&gt; that makes sure the bottom of an element containing floated elements falls below the contained elements. These mixins can be used like so:

&lt;script src="http://gist.github.com/26255.js"&gt;&lt;/script&gt;
&lt;p&gt;This ability to create abstractions using mixins is what makes Compass possible and what makes your life easier. Sass has many features that I cannot cover here, I highly encourage you to go read the &lt;a href="http://sass-lang.com/"&gt;Sass documentation to learn more&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Introduction to Blueprint&lt;/h3&gt;
&lt;p&gt;If you are familiar with the &lt;a href="http://www.blueprintcss.org/"&gt;Blueprint CSS Framework&lt;/a&gt;, you can now use it just as you would have without compass. All of the Blueprint classes are defined and available to you.
If you're not familiar with blueprint, I'll give a quick overview here:&lt;p&gt;

&lt;p&gt;Blueprint creates a 950px wide container that is divided into 24 units across. Each unit is composed of 30px of content space and 10px of gutter space. Using the &lt;code&gt;+column&lt;/code&gt; mixin you can define an element as a column that is some number of units wide. Columns are floated left and new rows are created by this floating whenever an element would extend past the container bounds. For this reason, we have to tell the last element on a row that it is the last one so that it knows to remove the 10px gutter on the right side and fit into the current row. When a columns is 24 units wide it is implied that it is the last column for the row and so it is safe to omit the last argument from those column declarations. Columns can be nested in which case the nested columns must add up the the width of the containing column and each row of nested columns must end with an additional last option. Please see the wiki for a complete &lt;a href="http://compass-style.org/docs/reference/blueprint/grid/"&gt;reference of the blueprint grid library&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Getting Semantic&lt;/h3&gt;
&lt;p&gt;However, the very reason that compass was created was to make it possible for us to avoid using presentational class names in our markup. So let's rip out a few things that we're not going to use to avoid enlarging our stylesheet unnecessarily.&lt;/p&gt;

Open up &lt;code&gt;src/screen.sass&lt;/code&gt; in your text editor and change it to this:

&lt;script src="http://gist.github.com/26242.js"&gt;&lt;/script&gt;
&lt;p&gt;This change removes the blueprint grid classes from our generated CSS, we don't need them because we're going to use the blueprint library's grid mixins with our semantic markup instead.&lt;/p&gt;

Let's briefly go over the changes we've made here:
&lt;ul&gt;
&lt;li&gt;First, we're only applying these styles to pages that have a body class of "blueprint". This is entirely optional, but as a best practice, I encourage you to always scope your styles by a body class or id so they don't appear where you intend them to.&lt;/li&gt;
&lt;li&gt;We've taken out the generic +blueprint mixin that adds the entire blueprint framework and replaced it with &lt;code&gt;+blueprint-typography&lt;/code&gt; which only styles our typography.&lt;/li&gt;
&lt;li&gt;We've told the class &lt;code&gt;.container&lt;/code&gt; that it is a blueprint container on body.bluprint pages.&lt;/li&gt;
&lt;/ul&gt;

The parts that didn't change:
&lt;ul&gt;
&lt;li&gt;@import directives make different sass libraries available to us. If those libraries have styles in them, those styles are added to our pages as is the case for the compass/reset.sass which will do a global reset to all pages that include this stylesheet. For more information on css resets, please read &lt;a href="http://meyerweb.com/eric/tools/css/reset/"&gt;Eric Meyer's Post&lt;/a&gt;. It is also possible to selectively reset only certain pages, but that is outside the scope of this post.&lt;/li&gt;
&lt;li&gt;the scaffolding import and the &lt;code&gt;+bluprint-scaffolding&lt;/code&gt; call makes our page look nicer as we're first getting started, but soon enough we'll want to pull these out because the styles it provides usually get in the way of a fully styled website.&lt;/li&gt;
&lt;/ul&gt;

Now we update our CSS files by running the compass command line while in the &lt;code&gt;my_compass_project&lt;/code&gt; directory.
&lt;pre&gt;&lt;code&gt;
$ compass compile
compile src/ie.sass
overwrite stylesheets/ie.css
compile src/print.sass
overwrite stylesheets/print.css
compile src/screen.sass
overwrite stylesheets/screen.css
&lt;/code&gt;&lt;/pre&gt;
Reload &lt;code&gt;index.html&lt;/code&gt; and things should look exactly the same.

&lt;h3&gt;Setting up auto-updates&lt;/h3&gt;
We'll be making a number of stylesheet changes going forward and having to run the compass command-line after each edit gets tedious. So compass provides a "watch" mode that will monitor your project for changes and recompile your stylesheets automatically. In your project directory run the following command:
&lt;pre&gt;&lt;code&gt;
$ compass watch
&lt;/code&gt;&lt;/pre&gt;
If you encounter an error, the details will appear at the top of your webpage.

&lt;h3&gt;Styling your First Page&lt;/h3&gt;
Now it's time to add some content to our page and style it. Let's update the container element in our index.html to the following:

&lt;script src="http://gist.github.com/26244.js"&gt;&lt;/script&gt;

We will now style this page to have a header, footer, sidebar and primary content area using blueprint grid mixins.

Open &lt;code&gt;src/screen.sass&lt;/code&gt; in your text editor change it to the following:

&lt;script src="http://gist.github.com/26263.js"&gt;&lt;/script&gt;

These sass mixins assign blueprint styles to our page that causes it to be laid out nicely. After saving, the css should have been automatically compiled for you. Go ahead and reload your index.html.

It looks pretty good, but I think the header needs a little cleanup so we'll apply some basic styling as well as some compass utilities to it:

First, let's pull in the compass utility library by adding an import:
&lt;code&gt;@import compass/utilities&lt;/code&gt;

And then amend the &lt;code&gt;#header&lt;/code&gt; style rules like so:

&lt;script src="http://gist.github.com/26265.js"&gt;&lt;/script&gt;
You'll notice we've floated the h1 left and the site actions right. I've also told the site-actions list to be a horizontal-list, which is a very handy utility mixin provided by compass. Reload the page, and I hope you'll agree that looks pretty decent.

Let's take a look at the css we've generated just for giggles:

&lt;script src="http://gist.github.com/26266.js"&gt;&lt;/script&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Well, you've seen how to style a page using Sass and Compass. We've only just scratched the surface here. I hope this was enough to get you started. From here you should go read the wiki, and ask questions on the mailing list if you get stuck.&lt;/p&gt;

&lt;h3&gt;See Also&lt;/h3&gt;
&lt;p&gt;If you found this post confusing (or maybe slightly out of date), there is a very nice &lt;a href="http://net.tutsplus.com/tutorials/html-css-techniques/using-compass-and-sass-for-css-in-your-next-project/"&gt;blog post here&lt;/a&gt; on how to get compass running and it explains some of the basics.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-1409529812666644458?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/1409529812666644458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=1409529812666644458' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/1409529812666644458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/1409529812666644458'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2008/11/compass-primer.html' title='A Compass Primer'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-1316111677590711185</id><published>2008-11-01T13:34:00.000-07:00</published><updated>2008-11-18T14:35:43.983-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open-source'/><category scheme='http://www.blogger.com/atom/ns#' term='sass'/><category scheme='http://www.blogger.com/atom/ns#' term='compass'/><title type='text'>Introducing Compass.</title><content type='html'>&lt;h4&gt;Pointing the way to cleaner, more maintainable stylesheets.&lt;/h4&gt;Are you stuck in a web of CSS Spaghetti? Are you tired of starting from scratch with every website that you build? Don't designers deserve tools to create abstractions just like their programmer buddies?

With compass you start with a vocabulary of common styling patterns and best practices all wrapped up and ready to go. With Compass, you don't write CSS! CSS was designed to make browsers efficient, not designers. Instead you write your stylesheets in a language called Sass and it gets converted to CSS for you. But don't let this frighten you. Sass was designed to make CSS easier.

So let's start with a really simple example. Ever wanted a list that was displayed horizontally instead of vertically? I thought so. In fact, you probably have a css class tucked away somewhere called "horizontal" that you can apply to lists to achieve this effect.

If you do, you've created an abstraction that you can apply to your html to achieve a desired presentation. This is practical and I understand why you've done this but that doesn't change the fact that you're doing it WRONG!

You've heard the CSS Experts talk about writing "Semantic" HTML and how you should keep your content and styles separate. But the power of abstraction to simplify your immediate task at hand always trumps the theoretical benefit of "doing it right." Using Compass and Sass, you no longer have to make this trade-off -- Doing the right thing now is just as easy as doing the wrong thing was.

So instead of doing this:

&lt;script type="text/javascript" src="http://gist.github.com/21569.js"&gt;&lt;/script&gt;

You can omit the non-semantic "horizontal" class and just add this to your stylesheet instead:

&lt;script src="http://gist.github.com/21572.js"&gt;&lt;/script&gt;

+horizontal-list is called a "Mixin". It adds a behavior to the "ol#menu" selector. A mixin can add styles and nested rules. So the above code generates the following CSS:

&lt;script src="http://gist.github.com/21573.js"&gt;&lt;/script&gt;

Aren't you glad you didn't have to write all that by hand?

&lt;h4&gt;Why does semantic markup matter?&lt;/h4&gt;
I'll direct you to a &lt;a href="http://www.caring.com/what-is-alzheimers"&gt;page on my website&lt;/a&gt; and point out that there's three menus on this page. There's a top menu for general navigation, a context menu on the left side, and a footer menu. Each of them looks completely different and yet the same code was used to generate the html for each one. The only difference is how they are styled. Certainly doable without Compass, but because our abstractions live in the stylesheets, it was no harder than styling custom generated html for each with the corresponding utility class names in the html markup.

&lt;h4&gt;We've only scratched the surface.&lt;/h4&gt;
There are about twenty utility mixins (and growing) found in compass to make the things you do every day as a designer easier. But compass is much more than a collection of utilities. Compass contains a complete port of the Blueprint CSS Framework. And if that's not enough, Compass is designed to work with other CSS frameworks as well and it will even let you mix and match them. For instance, at Caring.com we use YUI's font system and base styles but Blueprint's grid system.

&lt;h4&gt;Are you ready to get started?&lt;/h4&gt;
It's time to read the &lt;a href="http://acts-as-architect.blogspot.com/2008/11/compass-primer.html"&gt;Compass Primer&lt;/a&gt;. You can also head over to the wiki and read the &lt;a href="http://github.com/chriseppstein/compass/wikis/getting-started"&gt;Getting Started page&lt;/a&gt;.

&lt;h4&gt;Don't forget&lt;/h4&gt;Please follow the &lt;a href="http://github.com/chriseppstein/compass/tree/master"&gt;Compass project on github&lt;/a&gt; to stay up to date on developments and join the &lt;a href="http://groups.google.com/group/compass-users"&gt;Mailing list&lt;/a&gt; for major announcements and support. For now, please report any bugs you find to the mailing list.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-1316111677590711185?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/1316111677590711185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=1316111677590711185' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/1316111677590711185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/1316111677590711185'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2008/11/introducing-compass.html' title='Introducing Compass.'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-2505721827527232058</id><published>2008-11-01T12:29:00.000-07:00</published><updated>2008-11-01T13:32:59.932-07:00</updated><title type='text'>Back after a Hiatus</title><content type='html'>&lt;p&gt;It's been a year and a half since I joined &lt;a href="http://www.caring.com/"&gt;Caring.com&lt;/a&gt;. I've learned a whole hell of a lot during that time and instead of sharing my trials and tribulations of learning rails and building out a pretty darn complex website, I'm going to shift the focus of this blog to share how we're doing things that are different and unique.
&lt;/p&gt;&lt;p&gt;Caring.com engineers have produced no fewer than ten unique open source projects as part of our work on the site and have contributed features and bug fixes to numerous open source projects like &lt;a href="http://github.com/nex3/haml/tree"&gt;haml&lt;/a&gt;, &lt;a href="http://github.com/rails/rails/tree"&gt;rails&lt;/a&gt;, &lt;a href="http://xph.us/software/beanstalkd/"&gt;beanstalk&lt;/a&gt;, and &lt;a href="http://git.or.cz/"&gt;git&lt;/a&gt;. I'm pretty darn proud of us and I intend to start sharing more about the great things we do.
&lt;/p&gt;&lt;p&gt;We've just launched a complete redesign of the site. From a technical perspective, this launch was a resounding success. We have a very solid feature set that exceeded expectations, and we did this with just four engineers. As the architect, I did some up-front coding and planning and got our application infrastructure in place so that the rest of the team could hit the ground running. I spent about two months preparing while the others were building other features. In that time, I overhauled our core data model, implemented &lt;a href="http://github.com/chriseppstein/cells/tree/master"&gt;dramatic changes to the Cells project&lt;/a&gt;, created &lt;a href="http://github.com/chriseppstein/compass/tree/master"&gt;an entire stylesheet framework&lt;/a&gt; built on the awesomeness that is &lt;a href="http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html"&gt;Sass&lt;/a&gt;, and eventually ended up joining the HAML project as a core contributor. Needless to say, I've been busy and my family is glad that things are cooling off a bit.

&lt;/p&gt;&lt;p&gt;Seven weeks before launch we still didn't have a single template built but I had all the infrastructure in place when the full team finished their other work. Truth be told, I was freaking out and didn't imagine that we would hit our date. But 5 weeks later we were feature complete and two weeks after that we had all the bugs fixed and the site was looking better in all our target browsers than it had ever looked before. The engineers kept complaining that things were too easy and straight-forward and they didn't have to think enough when building out the features. In other words: music to my ears.
&lt;/p&gt;&lt;p&gt;In the coming weeks and months, I will be sharing with you as much as I can about how four engineers in five weeks rebuilt what amounted to a complete rewrite of the "core" of caring.com: Our content system. I will also be describing the open source projects Caring.com is contributing back to the open source community that has given so much to us. I hope you find it interesting and at least somewhat helpful.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-2505721827527232058?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/2505721827527232058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=2505721827527232058' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/2505721827527232058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/2505721827527232058'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2008/11/back-after-hiatus.html' title='Back after a Hiatus'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-8132734122874508003</id><published>2008-04-17T10:23:00.000-07:00</published><updated>2008-04-17T10:25:45.552-07:00</updated><title type='text'>Rails partial collection counter behavior changed</title><content type='html'>In migrating from rails 1.2 to 2.1 (edge), we found that the automatic counter is no longer a zero-based index, it starts with 1 now. I can see how that's a more useful default but it took a while to figure out why all our partial_counter == 0 conditions were failing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-8132734122874508003?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/8132734122874508003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=8132734122874508003' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/8132734122874508003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/8132734122874508003'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2008/04/rails-partial-collection-counter.html' title='Rails partial collection counter behavior changed'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-705125035263569883</id><published>2007-07-23T14:32:00.000-07:00</published><updated>2007-07-23T15:07:08.796-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SEO Optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='unique'/><category scheme='http://www.blogger.com/atom/ns#' term='URLs'/><category scheme='http://www.blogger.com/atom/ns#' term='uniqueness'/><title type='text'>Creating a Unique URL</title><content type='html'>SEO optimization these days requires that you have nicely-dashed-urls instead of ids. While there's a few examples of how to generate these on the web, I found all of them lacking in flexibility and reuse. So here's my solution to the problem. The approach decomposes the problem into three subproblems:
&lt;ol&gt;
&lt;li&gt;Eliminate unsafe characters from a string.
&lt;li&gt;Identify the uniqueness of a string.
&lt;li&gt;Generation of endings to construct uniqueness candidates.
&lt;/ol&gt;
You can then put them all together according to your needs. For example:
&lt;code&gt;&lt;pre&gt;
#Generate a unique name by adding integers to the end
uniquify(url_safe(name.downcase)) do |candidate| 
  Article.find_by_url_name(candidate).blank?
end
&lt;/pre&gt;&lt;/code&gt;
The code is formatted as a Ruby module that can be mixed in to your classes as needed. I hope you find it useful!

&lt;code&gt;&lt;pre&gt;
module UrlUtils
  # Makes a string safe for urls
  # Options:
  #   :replacements - a Hash of a replacement string to a regex that should match for replacement
  #   :char - when :replacements is not provided, this is the string that will be used to replace unsafe characters. defaults to '-'
  #   :collapse - set to false if multiple, consecutive unsafe characters should not be replaced with only a single instance of :char. defaults to true.
  def url_safe(s, options = {})
    default_regex = options.fetch(:collapse, true) ? /[^a-zA-Z0-9-]+/ : /[^a-zA-Z0-9-]/
    replacements = options.fetch(:replacements, { options.fetch(:char,"-") =&gt; default_regex })
    replacements.each do |replacement, regex|
      s = s.gsub(regex,replacement)
    end
    return s
  end
  
  # Generate integers
  # Options:
  #   :start =&gt; 1, The integer to start with
  #   :end =&gt; nil, the last integer to generate, when nil this becomes an infinite sequence
  #   :increment =&gt; 1, the amount to add for each iteration
  def int_generator(options = {})
    start = options.fetch(:start,1)
    last = options[:end]
    increment = options.fetch(:increment, 1)
    raise ArgumentError if increment == 0
    raise ArgumentError if last &amp;&amp; (increment &gt; 0 &amp;&amp; start &gt; last) || (increment &lt; 0 &amp;&amp; start &lt; last)
    Generator.new do |g|
      i = start
      loop do
        g.yield i
        return if !last.nil? &amp;&amp; (increment &gt; 0 &amp;&amp; i &gt;= last) || (increment &lt; 0 &amp;&amp; i &lt;= last)
        i = i + increment
      end
    end
  end
  
  # accepts a block that will be passed a candidate string and should return true if it is unique.
  # Options:
  # =&gt; :separator =&gt; "-", a string that will be injected between the base string and the uniqifier
  # =&gt; :endings =&gt; generator, a Generator that provides endings to be placed at the end of the base.
  #                           defaults to the set of positive integers.
  def uniquify(base, options = {})
    sep = options.fetch(:separator, "-")
    endings = options[:endings] || int_generator
    return base if yield base
    while endings.next? do
      candidate = base+sep+endings.next.to_s
      return candidate if yield candidate
    end
    raise ArgumentError.new("No unique construction found for \"#{base}\"")
  end
end
&lt;/pre&gt;&lt;/code&gt;

If you prefer you can get the code as a &lt;a href="http://pastie.textmate.org/81549"&gt;pastie&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-705125035263569883?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/705125035263569883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=705125035263569883' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/705125035263569883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/705125035263569883'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2007/07/creating-unique-url.html' title='Creating a Unique URL'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-7566030739296927037</id><published>2007-06-17T19:47:00.000-07:00</published><updated>2007-06-17T20:48:39.118-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='rubyonrails'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='DRY'/><title type='text'>DRYing up Unit Test Preconditions and Postconditions</title><content type='html'>It's good practice in unit testing to assert any preconditions (aka assumptions) relevant to the test and the expected postconditions (aka side-effects). I had a unit test that went something like this:

&lt;code&gt;&lt;pre&gt;
class TaggableTest &lt; Test::Unit::TestCase
  fixtures :tags, :items
  def test_tagging
    one = items(:one)
    one.tag_list = "tag1, tag2, tag3, tag4"
    assert_equal 0, one.tags.size
    assert_equal 3, Tag.count
    assert_equal 0, Tagging.count
    one.save
    assert_not_nil one.id
    assert_equal 4, Tagging.count
    assert_equal 4, Tag.count
    assert_equal 4, one.tags.size
  end
&lt;/pre&gt;&lt;/code&gt;

I didn't care for this code because of the redundancy of the assert equals. So I added some new asserts to TestCase in test_helper.rb and was able to convert it into the following:

&lt;code&gt;&lt;pre&gt;
  def test_tagging
    one = items(:one)
    one.tag_list = "tag1, tag2, tag3, test4"
    assert_changed(lambda {one.tags.size},
                   lambda {Tag.count},
                   lambda {Tagging.count},
                   :from =&gt; [2, 3, 2],
                   :to =&gt; [4,4,4]) { one.save } 
  end
&lt;/pre&gt;&lt;/code&gt;

assert_changed will execute the code blocks passed in prior to the execution of the primary block and again afterwards. It will then compare those values to the from and to values (optionally) passed in. If you don't pass in both :from and :to, assert_changed merely validates that the values changed.

&lt;code&gt;&lt;pre&gt;
  private
  def singleton_maybe(s)
    return s[0] if s.is_a?(Array) and s.size == 1
    return s
  end
  
  public
  def assert_array_or_svo_equal(expected, actual)
    expected = singleton_maybe(expected)
    actual = singleton_maybe(actual)
    assert_equal expected, actual
  end

  def assert_changed(*expressions)
    options = {}
    options.update(expressions.pop) if expressions.last.is_a?(Hash)
    initial = expressions.map {|e| e.call}
    assert_array_or_svo_equal options[:from], initial if options.has_key?(:from)
    yield
    subsequent = expressions.map {|e| e.call}
    initial.each_with_index { |i, n| assert_not_equal i, subsequent[n] } if not (options.has_key?(:to) and options.has_key?(:from))
    assert_array_or_svo_equal options[:to], subsequent if options.has_key?(:to)
  end
  def assert_unchanged(*expressions)
    initial = expressions.map {|e| e.call}
    yield
    subsequent = expressions.map {|e| e.call}
    initial.each_with_index { |i, n| assert_equal subsequent[n] }
  end
&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-7566030739296927037?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/7566030739296927037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=7566030739296927037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/7566030739296927037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/7566030739296927037'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2007/06/drying-up-unit-test-preconditions-and.html' title='DRYing up Unit Test Preconditions and Postconditions'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-1800890174309596070</id><published>2007-05-22T16:37:00.000-07:00</published><updated>2007-05-23T15:20:42.475-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CI'/><category scheme='http://www.blogger.com/atom/ns#' term='mercurial'/><category scheme='http://www.blogger.com/atom/ns#' term='buildbot'/><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='hg'/><category scheme='http://www.blogger.com/atom/ns#' term='continuousintegration'/><title type='text'>Continuous Build Integration</title><content type='html'>If you don't know what &lt;a href="http://www.martinfowler.com/articles/continuousIntegration.html"&gt;Continuous Build Integration&lt;/a&gt; is, then you should because it makes the development process work very well.&lt;br /&gt;&lt;br /&gt;Here at UnnamedStartup&amp;reg; we're using &lt;a href="http://www.selenic.com/mercurial/wiki/"&gt;Mercurial&lt;/a&gt; (a.k.a. Hg) as our revision control system and we wanted to use &lt;a href="http://buildbot.net/trac"&gt;buildbot&lt;/a&gt; to manage our continuous integration according to the following diagram:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_SN7npPwoqNg/RlOWfA8BGpI/AAAAAAAAAA0/mRNnv-oC9Z4/s1600-h/Continuous+Integration.png"&gt;&lt;img style="cursor:pointer; cursor:hand; background-color: white;" src="http://3.bp.blogspot.com/_SN7npPwoqNg/RlOWfA8BGpI/AAAAAAAAAA0/mRNnv-oC9Z4/s400/Continuous+Integration.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5067559465293781650" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've just got the closed loop from developer to Hg to Buildbot to email. It could have gone smoother. Here's a few of the challenges I faced, and how I solved them.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Sending Check-in Notifications from Mercurial to Buildbot&lt;/b&gt; - There is a user contribution to buildbot for doing this. You can download &lt;a href="http://buildbot.net/trac/browser/contrib/hg_buildbot.py"&gt;hg_buildbot.py&lt;/a&gt; and make sure it is executable to the user(s) commiting to your Mercurial repository. Follow the instructions in the comments of that file to have mercurial call this script. Please note that if you are submitting changes via https, I hit a &lt;a href="http://www.selenic.com/mercurial/bts/issue568"&gt;bug&lt;/a&gt; and ended up changing to committing via ssh to work around it (for what it's worth, ssh is faster than https).&lt;br /&gt;&lt;li&gt;&lt;b&gt;Configure Buildbot Sources&lt;/b&gt; - The hg_buildbot.py script is expecting buildbot to be accepting changes from a PBChangeSource. Configure buidbot like so:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;from buildbot.changes.pb import PBChangeSource&lt;br /&gt;c['sources'].append(PBChangeSource())&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Unified email recipients list&lt;/b&gt; - I've configured Mercurial to send email notifications like so:&lt;br /&gt;&lt;pre style="width: 370px; overflow-x: scroll;"&gt;&lt;br /&gt;&lt;b&gt;.hg/hgrc&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;...&lt;br /&gt;[hooks]&lt;br /&gt;#callback to the notifier extension when changegroups are constructed&lt;br /&gt;changegroup.notify = python:hgext.notify.hook&lt;br /&gt;&lt;br /&gt;[notify]&lt;br /&gt;#only send out emails if a changegroup is pushed to the master repository&lt;br /&gt;sources = serve&lt;br /&gt;# set this to True when you need to do testing&lt;br /&gt;test = False&lt;br /&gt;config = /usr/local/share/hg/my_email_notifications&lt;br /&gt;template = Subject: Changes in repository: {desc|firstline|strip}\nFrom: {author}\n\ndetails:   {baseurl}/rev/{node|short}\nchangeset: {rev}:{node|short}\nuser:      {author}\ndate:      {date|date}\ndescription:\n{desc}\n&lt;br /&gt;...&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;/usr/local/share/hg/my_email_notifications&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[reposubs]&lt;br /&gt;* = "Developer 1"&amp;lt;dev1@unamedstartup.com&gt;, "Developer 2"&amp;lt;dev2@unamedstartup.com&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So we now want to use the same list when telling our developers that the build failed. Since buildbot configuration file is just python we can embed this parsing code directly in our configuration file:&lt;br /&gt;&lt;pre style="width: 370px; overflow-x: scroll;"&gt;&lt;code&gt;&lt;br /&gt;emailcfg = open("/usr/local/share/hg/my_email_notifications")&lt;br /&gt;emailcfg.readline()&lt;br /&gt;import re&lt;br /&gt;emailparser = re.compile("&lt;(.+@.+)&gt;")&lt;br /&gt;emails = map(lambda s: emailparser.search(s).group(1),&lt;br /&gt;             emailcfg.readline().split("=")[1].split(","))&lt;br /&gt;emailcfg.close()&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Granted, this isn't going to handle changes to the my_email_notications file very well, but you should get the idea. The important thing is that we aren't maintaining two lists of emails.&lt;br /&gt;&lt;li&gt;&lt;b&gt;Sending email to an authenticating smtp server&lt;/b&gt; - Out of the box buildbot can only send email to an open SMTP server... I'm not sure who's dumb enough to leave their email server open like that, but we don't. So a little reading through the twisted libraries and I found that twisted kind-of supports ESMTP. I had to wrap this up in a buildbot notifier. Here's the code for that:&lt;br /&gt;&lt;pre style="width: 370px; overflow-x: scroll;"&gt;&lt;code&gt;&lt;br /&gt;from buildbot.status.mail import MailNotifier&lt;br /&gt;class ESMTPMailNotifier(MailNotifier):&lt;br /&gt;  def __init__(self, username=None, password=None, port=25, *args, **kwargs):&lt;br /&gt;    MailNotifier.__init__(self,*args,**kwargs)&lt;br /&gt;    self._username = username&lt;br /&gt;    self._password = password&lt;br /&gt;    self._port = port&lt;br /&gt;  def sendMessage(self, m, recipients):&lt;br /&gt;        from twisted.internet.ssl import ClientContextFactory &lt;br /&gt;        from twisted.internet import reactor&lt;br /&gt;        from twisted.mail.smtp import ESMTPSenderFactory&lt;br /&gt;        from StringIO import StringIO&lt;br /&gt;        from twisted.internet import defer&lt;br /&gt;        s = m.as_string() &lt;br /&gt;        ds = []&lt;br /&gt;        for recip in recipients:&lt;br /&gt;           if not hasattr(m,'read'):&lt;br /&gt;                # It's not a file&lt;br /&gt;                m = StringIO(str(m))&lt;br /&gt;           d = defer.Deferred()&lt;br /&gt;           factory = ESMTPSenderFactory(self._username, self._password,&lt;br /&gt;                                        self.fromaddr, recip, m, d,&lt;br /&gt;                                        contextFactory=ClientContextFactory())&lt;br /&gt;           reactor.connectTCP(self.relayhost, self._port, factory)&lt;br /&gt;           ds.append(d)&lt;br /&gt;        return defer.DeferredList(ds)&lt;br /&gt; &lt;br /&gt;from buildbot.status import mail &lt;br /&gt;c['status'].append(ESMTPMailNotifier(username="yourusername",&lt;br /&gt;                                     password="yourpassword",&lt;br /&gt;                                     fromaddr="you@yourcompany.net",&lt;br /&gt;                                     relayhost="smtp.gmail.com",&lt;br /&gt;                                     mode="all",&lt;br /&gt;                                     extraRecipients=emails,&lt;br /&gt;                                     sendToInterestedUsers=False))&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Granted, this isn't a complete guide to how to set up Mercurial and Buildbot, but I hope this will help you get over some of the minor hurdles I had to jump over.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-1800890174309596070?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/1800890174309596070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=1800890174309596070' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/1800890174309596070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/1800890174309596070'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2007/05/continuous-build-integration.html' title='Continuous Build Integration'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SN7npPwoqNg/RlOWfA8BGpI/AAAAAAAAAA0/mRNnv-oC9Z4/s72-c/Continuous+Integration.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-1154928800821175420</id><published>2007-05-21T16:09:00.000-07:00</published><updated>2007-05-23T15:11:40.319-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bugs'/><category scheme='http://www.blogger.com/atom/ns#' term='rubyonrails'/><title type='text'>Paranoid Versions</title><content type='html'>As I mentioned in my previous post, we have an Item object that is both versioned and remains in the database after it's deleted. Fortunately, there's two ruby plugins that do just that: &lt;a href="http://wiki.rubyonrails.org/rails/pages/ActsAsVersioned"&gt;acts_as_versioned&lt;/a&gt; and &lt;a href="http://ar-paranoid.rubyforge.org/"&gt;acts_as_paranoid&lt;/a&gt;. Unfortunately, these plugins &lt;a href="http://rubyforge.org/tracker/index.php?func=detail&amp;aid=8683&amp;amp;group_id=943&amp;atid=3697"&gt;don't work together perfectly&lt;/a&gt;. Even more unfortunately, this fix doesn't work as it should because acts_as_versioned tries to version the &lt;code&gt;deleted_at&lt;/code&gt; column as well, so &lt;a href="http://pastie.textmate.org/63403"&gt;here's the module&lt;/a&gt; we're using to combine them:
&lt;pre style="width: 370px; overflow-x: scroll;"&gt;
module ActiveRecord
 module Acts
  module Versioned
    module ClassMethods
      def acts_as_paranoid_versioned
        acts_as_paranoid
        acts_as_versioned    
        self.non_versioned_columns &lt;&lt; 'deleted_at'
        # protect the versioned model
        self.versioned_class.class_eval do
          def self.delete_all(conditions = nil); return; end
        end
      end
    end
  end
 end
end
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-1154928800821175420?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/1154928800821175420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=1154928800821175420' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/1154928800821175420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/1154928800821175420'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2007/05/paranoid-versions.html' title='Paranoid Versions'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-3128657440877631017</id><published>2007-05-16T23:49:00.000-07:00</published><updated>2007-05-22T18:39:59.719-07:00</updated><title type='text'>All Aboard!</title><content type='html'>Today at 1pm I started writing our first ruby model object. It is the Item base class and of course it's non trivial as far as model objects go because we have lots of non-standard behavior for it. But I don't like to start with the easy stuff, especially when I'm just learning a new framework and all... At 11pm I had the following working:
&lt;ul&gt;&lt;li&gt;   Item model and schema complete with data migrations so that anyone can start with an empty database&lt;/li&gt;&lt;li&gt;   CRUD support for Item&lt;/li&gt;&lt;li&gt;   output as html and xml&lt;/li&gt;&lt;li&gt;   Items are versioned. The complete edit history is stored in the database (each version is a complete record)&lt;/li&gt;&lt;li&gt;   Items are "paranoid". This means that when they are deleted, they are not really deleted -- instead the deleted_at timestamp is set.&lt;/li&gt;&lt;li&gt;   Concurrent editing detection, first submitter wins (error handling needed)&lt;/li&gt;&lt;li&gt;   textile formatting support&lt;/li&gt;&lt;li&gt;   listing of items using the &lt;a href="http://microformats.org/wiki/xoxo"&gt;xoxo microformat&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;What we don't have that we should:
&lt;ul&gt;&lt;li&gt;   Version viewing/comparing/reverting from the interface&lt;/li&gt;&lt;/ul&gt;
I took a few breaks for dinner at whatnot. I'd say I spent a sum total of 7 hours "programming" but in actuality, it was about 1 hour of programming and 6 hours of looking up things on websites and doing research on how to use these rails plugins. This included fixing an incompatibility between two of the rails plugins I wanted to use (acts_as_versioned and acts_as_paranoid).

it's hard to to much more with items until we make more models. My goal was to get enough underway that we could check in our initial project and set up continuous build, testing, and demo environments.

And that, my friends, is why rails is neat. An experienced rails developer would have finished all this before leaving the office and then do something more productive with his/her evening ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-3128657440877631017?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/3128657440877631017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=3128657440877631017' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/3128657440877631017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/3128657440877631017'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2007/05/all-aboard.html' title='All Aboard!'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5459321208870016810.post-861872864595228364</id><published>2007-05-15T16:00:00.000-07:00</published><updated>2007-05-22T18:40:29.471-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='rubyonrails'/><category scheme='http://www.blogger.com/atom/ns#' term='firstpost'/><category scheme='http://www.blogger.com/atom/ns#' term='architect'/><title type='text'>Getting Rolling</title><content type='html'>As chief architect of a angel-funded web startup that shall remain nameless for now, I've decided to use Ruby on Rails as our site platform. My early experiences with rails have been extremely positive and have so far, made me very productive -- in most part due to the great work that has been done by the rails community. I'll post my experiences and any useful information I learn here, in an attempt to give back to the community.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5459321208870016810-861872864595228364?l=acts-as-architect.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://acts-as-architect.blogspot.com/feeds/861872864595228364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5459321208870016810&amp;postID=861872864595228364' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/861872864595228364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5459321208870016810/posts/default/861872864595228364'/><link rel='alternate' type='text/html' href='http://acts-as-architect.blogspot.com/2007/05/getting-rolling.html' title='Getting Rolling'/><author><name>chris</name><uri>http://www.blogger.com/profile/03479650397003445694</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
