Organizing Stylesheets in a Rails Project

12 Oct

Clean up, clean up.

In the spirit of getting stuff done, CSS often takes a back seat when you’re building a new Rails application – and you can quickly end up with a messy CSS code base. At Chloe + Isabel, our technical debt has accumulated to the point where it’s time to roll up the sleeves and perform some CSS surgery.

Let’s start with the file structure. In Rails fashion, I wanted to create a convention that our development team can use that is familiar, flexible and has a minimal learning curve. So I tried to mimic Rails conventions as much as possible.

File Structure:

  • app/stylesheets
    • production.sass (this is the only file included by the layout, like a Rack endpoint)
    • base_styles/ (these are the building blocks of your application, like ActionController)
      • reset.sass
      • colors.sass
      • forms.sass
      • errors.sass
      • products.sass
      • sidebar.sass
    • application.sass (acts like application_controller.rb)
    • mixins/ (styles that keep your code DRY. kind of like ActiveSupport)
      • round_corners.sass
      • utils.sass (clearfix and similar site-wide utilities)
    • views/ (follows the Rails app/ directory structure)
      • frontend.sass
      • backend.sass
      • controller.sass
      • controller/
        • action.sass

Add class attributes to <body> that map to controllers and actions.

We can manage controller and action specific CSS by adding class attributes to the <body> tag.
<body class=”#{params[:controller].paramaterize}
#{params[:controller].paramaterize}-#{params[:action].paramaterize}”>

In the example below, the cart action in the OrdersController overrides the default product text color.

The body tag in our layout will look like:

<body class=”orders orders-cart”>

base_styles/products.sass (This is the base product style across the application.)


.products (base style)
  color: red

views/orders.sass You can override styles by controller.

.orders (controller)
  .products (base style)
    color: purple

views/orders/cart.sass You can override styles by action.

.orders (controller)
   .orders-cart (action)
     .products (base style)
       color: green

Just the beginning…

Finding the right structure that works for our team will be an ongoing process, so here are a few topics I plan to elaborate on:

  • Using base styles vs. application.sass
  • Naming color variables
  • Pattern libraries
  • and other issues we run into…

References:

  1. http://css-tricks.com/265-id-your-body-for-greater-css-control-and-specificity/
  2. http://jgn.heroku.com/2010/03/11/front-end-code-organization-for-your-complex-apps/
  3. http://codefastdieyoung.com/2011/03/css-js-organization-best-practice/

Product “Can Has” Personality and Innovation

2 Oct

I came across a great post on Hacker News today by Jason Shen, How to Give Your Product Personality, a response to a great post on Fred Wilson’s blog Minimum Viable Personality. Shen’s post gives a lot of great specific examples of personality in product.

Fred Wilson’s post features Grimlock, a fictitious online character with caveman grammar, who influenced the title of this post. According to Grimlock, the main steps to establishing a personality, or “HOW NOT BE BORING”, are as follows:

1. HOW YOU CHANGE CUSTOMER’S LIFE?

The way I see it, there are two ways in which you can change a customer’s life:

  1. React to a customer’s needs to give them the best possible experience. This is competition, in the sense that you are competing with other companies who provide a similar product or service.
  2. Give a customer an entirely new experience that never existed before. This is innovation.

2. WHAT YOU STAND FOR?

The core of what you stand for are your values and Zappos Core Values provides a great example. If your company does not have values: create a spreadsheet, ask everyone to add what they think are company values to it and take a vote.

3. WHO OR WHAT YOU HATE?

If you are focusing on competition, it makes sense to identify an enemy. However, if you are focused on innovation there should be no competition, at least NOT YET, for the product or service that you are creating.

Grimlock does actually touch on innovation, whether he realizes it or not, when he writes:

NO BE CHICKEN

CHICKEN LIVE IN CAGE. NO CAN HAVE PERSONALITY INSIDE CAGE.

LAST STEP IS SMASH CAGE, LIGHT BARN ON FIRE.

DO THAT, YOU WIN.

If every other chicken is inside the cage and you focus on competing with other chickens, you may never break out of your cage. If you smash the cage and burn down the barn, you’ve changed the world of all the other chickens, you’ve innovated.

ORM’s tradeoff

30 Sep

The other day a fellow colleague was in the midst of working on one of the more mundane tasks a developer can do, a tabular report, when a discussion arose around the way the representation of the report data. It occurred to us that we were dealing with was sets of data merged into lists; the precise domain of SQL. Ruby, our language of choice, has great native support for sets and lists. So, why even bother using ActiveRecord? Just like object oriented programming, ORM has proven easy to grok and extremely useful, but can start to become a crutch or security blanket. For example, how many times have you spent reverse engineering a SQL statement into your ORM’s representation of it? ORM is an incredibly useful abstraction albeit a leaky one. This is hardly a new thought, but it is one that should be brought up every once and while lest it becomes a cyclical debate the likes of centralized vs client/server computing. The question we eventually had to answer was: what cost are we willing to pay for this abstraction?

As much as one may not want to admit it, eventually you will be faced with the stark reality that your ORM is actually getting in the way and you will need to write the SQL yourself. This is a direct result of the leaky-ness of ORM. It may be inefficient queries, constant metadata queries, or just the creation of large object graphs that will turn and punish your blind trust in the framework. In order to plug this hole most every ORM uses the same last resort, the option to pass raw SQL to the DB. In ActiveRecord there is connection.execute(), Hibernate has session.createSQLQuery() and LINQ for C# has ExecuteQuery(). Now, you may think this is useful for those little esoteric queries like ‘SELECT 1 FROM DUAL’, but that’s easily done with an ORM. It’s when you start to select large datasets that most ORMs start to crumble and you need the precision of execute() to get the job done.

There are alternatives to diving down into raw SQL. You could retrieve simple object graphs and combine them in the application tier. You could even roll your own DAO and return sets and lists. You could embrace NoSQL and just marshall objects right out of the database. This, however, would ignore what SQL is great at, set operations.

So why are we, the Bird Herd, willing to pay the price of the ORM abstraction? First, we want all our logic in the same place. We are early stage and in constant flux, and when the business logic changes it’s a comfort to know that we have just one context in which to work.

Second, ORMs tend to be easy to use and we can work quickly. 95% of what we do does not involve crazy joins across alarge complicated schema, or the kind of query where selecting a column we may not use incurs meaningful performance penalties.

Finally, ORMs have dealt with the harder problems like locking, transactions and connection issues for a long time. These issues are pretty much a solved problem and we have better things to spend our time on. We are a rapidly growing firm seeking to innovate in our market; we are not out to reinvent the wheel, we want to replace it.

A git revert alternative

26 Aug

We use gitflow for our branching model. Yesterday we ran into an issue where a front-end redesign was finished, merged into develop and pushed to remote, prior to being approved. This would cause a problem if we wanted to create new releases from develop because they would include the front-end redesign that wasn’t ready just yet. The immediate reaction was to use git-revert to rollback each individual commit, but it seemed like a bit of a sloppy solution for 20 or so commits.

The solution we came up with was the following:

  1. branch from the commit prior to the first unwanted commit, we named it develop_new
  2. cherry pick any commits you do want from develop into develop_new
  3. rename develop to feature/redesign
  4. rename develop_new to develop
  5. update .git/config to ensure the renamed branches point to the correct remote references
  6. force push develop to remote
  7. push feature/redesign to remote
This is obviously only a good solution if you catch the problem in time and thus only require a few cherry picks.
A bit of Googling produced a similar alternative using git-rebasehttp://stackoverflow.com/questions/4434787/git-how-to-undo-a-merge