Get the page node in hook_page_alter in Drupal 7

Many times, you need to access the node in hook_page_alter(). Traversing gets ugly, so I moved it out into this little helper.

1
2
3
function _get_page_node($page) {
 return (isset($page['content']['system_main']['nodes'])) ?  $page['content']['system_main']['nodes'][array_shift(array_keys($page['content']['system_main']['nodes']))]["#node"] : false;
}

Drop this is template.php, and now you can do something like

1
2
3
4
5
6
function YOURTHEME_page_alter(&$page) {
  $node = _get_page_node($page);
  if($node && $node->type == "article") {
    // ... edit only article nodes ...
  }
}
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

No Comments »

, ,
Code

How to contribute to Drupal

While up in Connecticut last weekend speaking at DrupalCampCT, I had the opputunity to speak with Ben Melancon (mlncn) about what we can do to make contributing more accessible to Drupal users of all skill levels. We all love that Drupal is very much a “plug-and-play” CMS, but getting those plugs made requires a lot of work and coordination. What I am trying to do is help streamline the process, so there is an extremely clear path on how to contribute to Drupal core and modules. As a first attempt, I’m working towards establishing an Un-official guide.

Please leave a comment with links to Drupal resources that you feel have been helpful in successfully contributing to Drupal: a guide/tutorial, feedback from a module maintainer, … anything really. After gathering a list, I will work to have seasoned vets of Drupal review the list, and pair it down to the best representation of the way to contribute.

Thanks.

Drupal Contributors Guide

Making a Drupal patch with Git – http://drupal.org/node/707484
Submitting patches – http://drupal.org/patch/submit

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

2 Comments »

Information

Using Hash Bangs in your URLs

Ever since the emergence of AJAX into web commonplace, we have been looking for feasible ways to may the content crawl-able by spiders. Recently, you may have noticed a handful of websites with strange looking URLs, something like this:

http://www.example.com/#!some/unique/identifier

Why would anyone do such a thing? The answer should be obvious, Google. In a guide released on Google Code, they suggest creating “pretty URLs” that allows Google to find your “web snapshots.” Among the big players using the URL scheme is Twitter and Facebook, which inevitably means it started to spread as a sure-fire, absolutely perfect way of getting AJAX-ed content crawled.

We’ll, like most truths, this isn’t completely true. As a matter of fact, there is hardly ever a time you want to use this scheme. As Mike Davies awesomely outlined, there are so many disadvantages to using the scheme, and most of the advantages are myths (I’ll wait here ’til you go read that).

For the lazy, to summarize, you are in essence making a client-side front-controller: the “#” in the URL breaks it into a resource (example.com) and a fragment (!some/unique/identifier) – the fragment is used as the unique identifier for what content to AJAX in, but it always routes through the same resource. It’s as if your website only has one page, that goes against all of our traditional SEO rules. Additionally, this AJAX scheme is not a standard, it’s a Google facility – meaning you’ve now ousted every other spider – kudos!

What about private web apps?

Mike Davies article was awesome, and you should really take to heart his guidance about not using hash-bangs in public sites. However, I wanted to begin a tangent off of a topic that Davies brought up – using hash-bangs in web applications. In his article he writes:

“Engineers will mutter something about preserving state within an Ajax application. And frankly, that’s a ridiculous reason for breaking URLs like that.”

Given his context, publicly searchable content, I’ll give him that. But the land of private, SaaS web applications, where the crawl-ability of your content isn’t a requirement or concern, let’s re-evaluate:

  • Using hash bang URLs prevents non-Google crawlers from consuming AJAX content, but a login system prevents them all from reading it! Crawl-ability is not a concern.
  • Using #! preserves back/forward buttons – for web apps, our main concern is UX, and using hashes is a native browser navigation feature, as opposed to binding to the forward/back buttons.
  • Using hashes allows you to develop an AJAX navigation framework using window.location.hash

To wrap up, Mike Davies definitely hit it on the head. In regards to using hash-bang URLs for public content sites … don’t. But for private web applications, the land of make-it-work, hash-bangs can provide some much needed functionality to accommodate AJAX navigation. I’d love to open the floor up to some other pros/cons or solutions about using hash-bangs in SaaS web applications.

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

No Comments »


Information

Configure Test::Unit and Capybara

While trying to set up Capybara with Test::Unit, I ran into trouble finding documentation on how to do it. Ultimately, I found the answer in the gem’s README, but for those who just want to google-and-go, I figured I’d post this for you.

Simply, throw this in your Gemfile

1
2
3
group :test do
  gem 'capybara'
end

and run `bundle`

Now, open up test/test_helper.rb: we’re going to require capybara, and then crack open the integration test class to include Capybara and pass it your app

1
2
3
4
5
6
7
8
# test_helper.rb
...
include Capybara

class ActionDispatch::IntegrationTest
  include Capybara
  Capybara.app = Issuepad::Application
end

That should do it, let me know if you run into any other issues.

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

4 Comments »

Code

Fixing the “You have to install development tools first” error with Nokogiri.

After wracking my brains around a strange issue installing Nokogiri, wmoxam in #rubyonrails and I figured out how to get it to install.

Problem: You go to install nokogiri, either with bundler or with a straight “gem install nokogiri”, and you get an error like the following:

In your wisdom, you decide to jump over to Aaron Paterson’s Installing Nokogiri page, where he tells you to install the libs, libxml2 and libxslt, manually and configure the gem install to those libs with flags … but it still doesn’t work.

Solution: You are probably using RVM, right? Yeah, we’ll when you installed ruby 1.9.* with it, it was probably old. Update your rvm install and re-install ruby with the new RVM version, and you will be green.

1
2
3
4
5
6
7
8
rvm update head
rvm --force install 1.9.2
gem install bundle

## in your rails app
bundle
Installing nokogiri (1.4.2) with native extensions
 ...

Boom!

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

6 Comments »

, ,
Information

ReadOnly needs to be set to true or false, not :true or :false

Just taking some time to let you in on another gotcha. Hopefully, this saves people some aggravation some day.

While working on Kludge, we took a RESTful approach and built our controller like so:

1
2
3
4
5
class ProjectsController < ApplicationController
  def update
    @project = Project.find(params[:id]).update_attributes(params[:project])
  end
end

While this works, it doesn’t offer you any protection that evil users may try to update projects that don’t belong to them. A better approach is to scope the projects to that belong to the account. So we updated the action to the following:

1
2
3
4
5
class ProjectsController < ApplicationController
  def update
    @project = current_account.projects.find(params[:id]).update_attributes(params[:project])
  end
end

…and we got an error!

1
2
ActiveRecord::ReadOnlyRecord (ActiveRecord::ReadOnlyRecord):
  app/controllers/projects_controller.rb:45:in `update'

This error arises because Rails makes all associations read-only. Since projects is now an association to account, we need to force readonly to false in the account model:

1
2
3
4
class Account < ActiveRecord::Base
  has_many :clients
  has_many :projects, :through => :clients, :readonly => :false
end

However, this will still throw the same error message! After googling, IRC-ing, and crying … I finally got the answer through brute-force trial and error. Turns out, :readonly does not accept the symbol :false, and only the literal false. Changing the following will make your test green:

1
2
3
4
class Account < ActiveRecord::Base
  has_many :clients
  has_many :projects, :through => :clients, :readonly => false # <-- no longer a symbol
end
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

2 Comments »

,
Code

Before Type Cast

While working on Kludge, I ran into a bit of a gotcha that I thought I’d share with everyone.

In our invoicing system, we have a Invoice model that has many LineItems. These line items, have a quantity attribute, and a quantity_type. This way, your line item can say “10 hours”, or “5 products”. When storing a quantity of hours, we wanted to actually store minutes (like 150), and then do some work on the reader when accessing that value (like 1.5) only when the quantity_type was :hour.

The Code

We thought it would be best to run a simple before_validation that checks whether or not the quantity being saved was to be saved in hours, and if so, convert it to minutes. Something like this:

1
2
3
4
5
6
class LineItemTest < ActiveSupport::TestCase
  def test_quantities_change_based_on_quantity_type_when_read
    @li = LineItem.create(:quantity => 0.5, :quantity_type => :hour)
    assert_equal(30, @li.read_attribute(:quantity))
  end
end

Our before validation looks like this:

1
2
3
4
5
6
7
8
9
10
11
class LineItem < ActiveRecord::Base

  before_validation :convert_quantity


private
  def convert_quantity
    logger.warn("-----#{quantity}")
    self.quantity = (quantity_type.to_sym == :hour) ? (quantity * 60).to_i : quantity
  end
end

This code was failing, and looking at our logs showed something strange:

1
2
-----0
  SQL (0.4ms)  INSERT INTO "line_items" ("created_at", "description", "invoice_id", "price_in_cents", "quantity", "quantity_type", "updated_at") VALUES ('2010-11-13 19:04:25.813989', NULL, NULL, NULL, 0, 'hour', '2010-11-13 19:04:25.813989')

Note the “—–0″: that zero is the value we are getting for quantity, before we even run any code. So what’s wrong?

Typecasting and *_before_type_cast

As it turns out, ActiveRecord will typecast your data before you get to it, and since our quantity column is an integer, the value of 0.5 was cast into 0. To correct this error, you can use the dynamic method *_before_type_cast to get the value …. well …. before type casting.

Updating the validation to:

1
2
3
4
private
  def convert_quantity
    self.quantity = (quantity_type.to_sym == :hour) ? (quantity_before_type_cast * 60).to_i : quantity
  end

… gave us our passing results. Hope this helps!

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

No Comments »


Uncategorized

Concerning Yourself with ActiveSupport::Concern

Picking up on our last talk about working with the Rails core, I wanted to take some time to introduce you to the internals of Rails 3, in hopes to break down any fears about hacking around in Rails. We are going to talk today about ActiveSupport::Concern.

Preface

For those of you new to ActiveSupport, let’s take a step back. Have you ever worked on a codebase and had to constantly do some utility work, like encoding or decoding JSON, generate a random number, or encrypt data? Of course, we all have, and the Rails core developers are no different. ActiveSupport is a library of such utilities that you are free to use not only in Rails, but in your own standalone Ruby project! I feel that ActiveSupport is the best place to start learning Rails core code: in core libraries, it takes multiple classes and modules to see a functionality come to life. Compounded by your unfamiliarity with the idioms that the core developers use, you may quickly find yourself getting lost. In ActiveSupport, most of the modules standalone, so you can look at the one file to find out what it’s doing (many helper modules are < 200 lines of code with comments). Also, it gives you small doses of such idioms, so you can get comfortable with core design patterns before diving into heavy lifting code.

::Concern

So now that you know about ActiveSupport, you won’t be surprised to learn that ActiveSupport::Concern is just another helpful utility module. But you might not understand what it’s useful for until you learn a little bit about a common Rails metaprogramming design pattern.

Mixing Class and Instance methods into your classes

Commonly in Rails, we use 3rd-party gems to add certain functionality to our classes; most commonly of which is ActiveRecord::Base. These gems usually add methods to instances of AR::Base, and to the class itself. For example, a tagging library might add an instance method @blog.tags, and a class method Blog.find_by_tags. If you’ve never looked under the hood to see how libraries do this, it may be look a little roundabout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module TagLib
 
  module ClassMethods
    def find_by_tags()
      # ...
    end
  end
 
  module InstanceMethods
    def tags()
      # ...
    end
  end
 
  def self.included(base)
    base.send :include, InstanceMethods
    base.send :extend, ClassMethods
  end
 
end

class ActiveRecord::Base
  include TagLib
end

This is a common Rails idiom that many developers are used to seeing. The system breaks down like this:

  1. Start on line 1, where we create a module, ours is TagLib, which will provide tagging functionality
  2. On line 23, we open up ActiveRecord::Base (AR::Base) and include our module. This will make the TagLib library available to all AR::Base classes. Note: this code that opens AR::Base is not inside of our module
  3. On line 15, we override the
    1
    self.included()

    method.

    1
    self.included()

    is a special “callback” method that gets automatically called when ever the module is included into something. In our case, we included TagLib into AR::Base, so this method will be called. self.included() takes a parameter, which is a reference to the class that included in it, in our case AR::Base. We can now take that reference, and use it to add methods to it. Note: we use

    1
    base.send :extend

    , instead of the basic

    1
    extend

    , to get around private method hiding.

  4. By calling self.included() in your class, it will include all of the instance methods of the class with the methods in the InstanceMethods module, giving your @blog.tags, and it will add all of the methods in the ClassMethods module to the class, so you can do Blog.find_by_tags.

There are many reasons why this system is a little bit hacky, and more will become apparent when you dive deepy into the Ruby language and metaprogramming. A couple standouts are:

  • You are overriding the
    1
    self.included()

    method to act like an extend method

  • When you include a method into a class, the methods automatically become apart of all instances in the class. It is not always necessary to have an InstanceMethods module to include another module.
  • It’s not readable, people have to struggle just to figure out this whole bootstrapping process.

Note: Smarter people have explained this way better than me

ActiveSupport::Concern to the rescue

Looking for a way to keep the same design pattern, but abstract the complexities out of the code, Josh Peek wrote ActiveSupport::Concern, which allows you to pull off our same TagLib module by doing something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module TagLib
  extend ActiveSupport::Concern

  module ClassMethods
    def find_by_tags()
      # ...
    end
  end
 
  module InstanceMethods
    def tags()
      # ...
    end
  end
end

class ActiveRecord::Base
  include TagLib
end

AS::Concern will look for modules named ClassMethods and InstanceMethods and bootstrap them as you normally would like. The module has some other nice benefits, for instance, sometimes you want to add other code in the

1
self.included()

method, such as logging:

1
2
3
def self.included(base)
  logger.warn("Adding TabLib - this will make you class awesome. Proceed with awesomeness")
  #...

Since AS::Concern removes the need for that call, it also provides you with an

1
included()

method that takes a block, so now you can do:

1
2
3
included do
  logger.warn ...
end

Hopefully, you’ve gotten a taste of some Rails idioms, Rails core code, and an explanation of a core module. I assure you, there are many more in ActiveSupport that are equally as straightforward to follow, so get hacking! (check out GZip, Buffered Logger, and Message Encryptor)

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

8 Comments »

Uncategorized

Getting Comfortable Working With Rails Core

This year’s RailsConf was truly one of the most impressive and inspiring development conferences I have ever been to. Bright-eyed and bewildered, I got to listen to lectures from people big and small in the community. Two individuals, in particular, stood out to me: Yehuda Katz and Rick Martinez. I listened to Yehuda’s keynote; the now Rails hero’s mentioned in his talk “I was just some guy who dove in, and here I am.” Rick Martinez of Flavorpill.com, who you may not know, is really just a guy who did dive in and was able to give a fascinating talk called “Hardcore Extending Rails 3.” The overall message? Again, “I just started digging around, and here’s what I came up with.”

As an aside, one of the questioneers at Rick’s talk was Yehuda commenting something to the affect of “this is what we wish more people would do, just dive in.”

I knew I had what it takes to dive in, it was just a matter of discipline and taking the time to learn the tools and get acclimated with the libraries. I walked into the conference feeling like a novice about to hear four days of lectures that were way over my head, and I left chomping at the bit waiting to start tearing to guts of Rails 3 wide open (which I began to do the entire trainride home).

For the past two months since then, I’ve been consuming more Ruby information then ever, determined to master the language and become a productive member of the Rails community. There have been frustrating times when I didn’t understand anything (and there will be more of that to come), but there has also been a wealth of knowledge that I have gained and have been able to apply immediately. For those of you who want to dive in, I’ve assembled this list of things I’ve done to get going and I think it could help you out, too. Pick and choose as you wish; these items may not all be for you, but if it’s on this list, it’s because I directly associate it with my growth.

  • Read The Ruby 1.9 Book

    If you want to work with Rails, you have got to learn Ruby. Ruby is filled with new concepts and techniques that you probably haven’t experienced in other languages. The more of this language you know, the more you will understand the design patterns that the Rails library uses, and the more power you can leverage in your own code. The internal architecture of the code is a lot to take in, you don’t want to be hung up on Ruby syntax at the same time. Learning Ruby will help you separate whether what you are looking at is confusing because of your lack of understanding of the Ruby code or the Rails library (looking at the Rails lib after learning Ruby better, the code is so much easier to follow). Read “The Ruby 1.9 Book,” also known as “The Pickaxe Book.”

  • Learn Ruby Metaprogramming

    Metaprogramming is the practice of writing code that writes code. Two weeks ago, I would have told you that Metaprogramming is something that the pros did because they’ve grown so bored with the boundaries of the language that they want to add to it. On the contrary, metaprogramming is a staple of working with Ruby and is a compliment of the language’s aesthetics. Learning about concepts such as Open Classes, Dynamic Dispatch and Methods, and beyond will help you understand how programmers wrangle Ruby to get it to work the way they want it to.

    Here are three great resources resource for learning Ruby metaprogramming:

    1. “The Ruby 1.9 Book” book has entire chapter on metaprogramming. I recommend reading this before jumping into…
    2. “Ruby Metaprogramming” by Paolo Perrotta. This book explains metaprogramming concepts in a fun, observational way, with an entire section on metaprogramming Rails.
    3. As a follow up, Yehuda wrote an article that helps clear up some common misconceptions about how to properly write libraries – “Metaprogramming In Ruby”

    By now you realize you’ve got a lot of reading to do, which is why I totally recommend that you…

  • Buy an iPad

    This may seem like one of those “you-got-to-be-kidding-me” kind of todos. But the truth is, I single-handedly attribute so much of my educational growth over the past month to me buying the iPad. Last year, I bought the Ruby 1.9 book and the PDF. The book sat on my shelf and I only casually browsed through it. The PDF was on my laptop, and I only fired it up a couple of times, and then get distracted by a client’s email, or a blog post, and ultimately didn’t read any of it. With the iPad, I take it everywhere: I read for 15 minutes at the laundry mat, 30 minutes at then pool, before I go to sleep, etc. In three weeks, I read over 450 pages of the Ruby 1.9 book and over 250 pages of the Metaprogramming book. Here is the key though: make it an educational device only. For me, that meant not loading on a Facebook app or any other distractions, I didn’t even configure my mail account or add music to it. It’s simply there for ebook reading, reading RSS feeds and Tweets from other developers, and writing blog posts. It’s a productivity machine!

  • Read the Rails source code

    While it may seem daunting at first, the current version of the Rails 3 library is one of the most elegant codebases to try and follow. Before taking the plunge into the codebase myself, I used to hear people say “look at the source” and I used to think “I am not at that level yet.” Trust me, you are, and the more comfortable you get with Ruby, the more you will learn how the Rails code plays into it’s strengths. Additionally, the library is filled with extremely helpful comments. For example, check out base.rb in ActiveRecord; don’t quote me on this, but I’m pretty sure for every line of code, there are two or three lines of comments.

  • Get Involved

    When it comes to getting involved with Rails, you couldn’t ask for a better community to help you out then the Rails community. There are people with all different levels of expertise, willing to lend a hand to help you achieve whatever you’re trying to accomplish.

    • Go to conferences and meet ups. Not only do you learn about emerging technologies, but you also get to network and make friends with other developers, as well as learn about other popular developers in the community. One key piece of advice: don’t be shy. Everyone is there for the same reason, and everyone I’ve gotten to meet in the Ruby community have been nothing but nice and helpful.
    • The Ruby on Rails IRC on freenode, #rubyonrails. Don’t feel shy; just ask your question and be patient. Feel free to hit me up in there, I’d be more than happy to help you out.
    • RailsBridge – RailsBridge is a group of people committed to helping you learn more about Rails. They can answer your questions in the #railsbridge IRC, they organize weekend bug mashes to help the Rails core team get through ticket issues in Lighthouse, and are really just a great group of people. I got to spend a good half hour chatting with Santiago Pastorino of WyeWorks at RailsConf, about RailsBridge and all the things they do, awesome stuff!
    • Read up on the work other developers are up to. Guys like Aaron Paterson, Ilya Gregorik, and Yehuda Katz will commonly blog or tweet about things they are working on. You can learn a lot by being a fly on the wall.

    The biggest piece of advice I can give you in terms of getting involved with the Rails codebase is: start. Any new library takes time to learn, and Rails is no exception. If you follow any or all of the tips I’ve listed, I think you’ll find the learning curve to be a little less steep. If you have any other advice that helped you out along to way, please leave a comment below, myself and others would appreciate it.

    • Digg
    • del.icio.us
    • Facebook
    • Google Bookmarks
    • Twitter

4 Comments »


Lecture

‘Mail’ is a resevered model name in Rails 3

This may bite you in the ass if you are upgrading a Rails app from 2 to 3: ‘Mail’ is now a reserved model name. That means, if you currently have a model app/models/mail.rb, and/or a class defined as class Mail < AR::Base (for instance), it will collide with the new Mail class in Rails 3, and possibly throw this error

1
undefined method `transfer_encoding' for class `Mail::Message'

That is because Rails defines Mail.transfer_encoding in it’s Mail class, and your’s is over-riding it. The quick fix: rename your class and your file name.

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Twitter

No Comments »


Code