Synthesis : Scott Becker

Active Record Relationship Design Patterns

In the world of web applications, eventually you’ve seen it all, and you start to see the same relational patterns occur over and over. To help out the newbies, here’s a list of various relationships (in Ruby on Rails syntax) that we see all the time.

Can you think of any more common relationship patterns?

Customer Relationship Management

class Company < ActiveRecord::Base
  has_many :contacts
end

class Contact < ActiveRecord::Base
  belongs_to :company
end

E-Commerce

class Category < ActiveRecord::Base
  has_many :products
end

class Product < ActiveRecord::Base
  belongs_to :category
end

class Order < ActiveRecord::Base
  has_many :line_items
end

class LineItem < ActiveRecord::Base
  belongs_to :product
  belongs_to :order
end

Roles Based Access Control

class User < ActiveRecord::Base
  has_and_belongs_to_many :roles
end

class Roles < ActiveRecord::Base
  has_and_belongs_to_many :users
  has_and_belongs_to_many :permissions
end

class Permission < ActiveRecord::Base
  has_and_belongs_to_many :roles
end

Mailing Lists:

class MailingList < ActiveRecord::Base
  has_many :subscribers
end

class Subscriber < ActiveRecord::Base
  belongs_to :mailing_list
end

Surveys / Questionaires / Quizzes:

class Survey < ActiveRecord::Base
  has_many :questions
  has_many :responses
end

class Question < ActiveRecord::Base
  belongs_to :survey
  has_many :choices
end

class Choice < ActiveRecord::Base
  belongs_to :question
end

class Response < ActiveRecord::Base
  belongs_to :survey
  has_many :response_choices
end

class ResponseChoice < ActiveRecord::Base
  belongs_to :response
  belongs_to :question
  belongs_to :choice
end

Hierarchical Content Management:

class Page < ActiveRecord::Base
  acts_as_tree
  acts_as_list :scope => :parent
end

Blogs:

class BlogEntry < ActiveRecord::Base
  has_many :comments, :dependent => true, :order => "created_at ASC"
end

class Comment < ActiveRecord::Base
  belongs_to :blog_entry
end

Social Networking:

(from: wiki.rubyonrails.com)

class User < ActiveRecord::Base
  has_and_belongs_to_many  :users,
                           :join_table => 'users_known_users',
                           :foreign_key => 'known_user_id',
                           :association_foreign_key => 'user_id',
                           :after_add => :create_reverse_association,
                           :after_remove => :remove_reverse_association

  def known_users
    self.users
  end

  private
    def create_reverse_association(associated_user)
      associated_user.known_users << self unless associated_user.known_users.include?(self)
    end

    def remove_reverse_association(associated_user)
      associated_user.known_users.delete(self) if associated_user.known_users.include?(self)
    end
end

Any major ones I’m missing here?

Comments Off on Active Record Relationship Design Patterns

up and away

Sweet, I’ve finally managed to upgrade to the latest Typo release (2.6.0) and move this blog to Dreamhost. I just couldn’t get my sites to stay up on Textdrive, so unfortunately I’m having to move away. I really like TxD, with the cool hacker culture, the active forums and direct association with rails, but I just found them to be too unreliable. More often than not I would go to my sites and find them down, or getting a random error. I realize its a challenge in a shared hosting environment, with lots of developers trying random things, but I haven’t been having these issues at Dreamhost, so here we are.

Comments Off on up and away

up down up down

My textdrive websites (including this blog ) have been going down as much as they have been staying up. For no reason, whatsoever. As you can imagine, this is immensely frustrating. At some point in the past I setup Daedalus to detect the absence of a lighttpd process and restart it, but i realized today that the daedalus process wasn’t setup as a cron job, so it wasn’t running, thus my lighttpd instances weren’t getting restarted. I’ve been restarting them manually lately, whenever i discover the sites are down, which seem to pretty much daily. Why lighttpd suddenly ceases to run, I have no idea. I have lighttpd setup as a cron job to start every time the server boots, but this apparently insures nothing. Add to that the fact that the server – Pendrell – seems to cycle all the time, and goes down a lot. It was down yesterday. Now that Daedalus is cron’d, hopefully things we’ll be up a bit more!

We’ll I got a second account at Dreamhost, because A – they are cheaper, B – they support rails, C – why not, they had a sale. I have followed their instructions and got rails up and running. One thing that puzzles me though, despite the fact that my sites are running in production mode on Dreamhost, it seems like it is reloading everything on every request. (Unlike lighttpd on textdrive, i dont have to start any process, you just drop the right files in the directory, give the public folder execute permissions, and you’re off to the races.) BUT, is it caching anything? Keeping any ruby processes alive? If I change a file, i dont have to stop/start, it simply sees the change. And seems a bit slower than textdrive, like its reloading every time. But this contradicts the idea of production mode, where things are supposed to be cached. Is there a way to get things caching in memory, and boost the speed a bit? I hope someone can enlighten me.

Comments Off on up down up down

acts_as_awesome

Ruby on Rails continues to amaze. Things just keep getting easier. With the file_column mixin, and the new acts_as_taggable mixin, generous people in the RoR community are quickly abstracting away all of the code thats generally a pain in the ass to write, and making things more and more fun. I just implemented file_column today, easy peasy. As well as a little RMagick for automatic thumbnailing of images.

With all this sophisticated functionality becoming so easy to implement, in 6 to 12 months I can imagine the average small business website being much more full featured, and possess what today is only for the big dogs and the super technical. Users will assume its a given and expect these features.

Best of all, the technology is free, contributed by the open source community. Things happen fast. No waiting five years for the next version. It gets better every day. If for some reason, someone wants to spend thousands of dollars for Windows Server, .NET, Visual Studio, SQL Server, and on and on, so they can write 10 times the code to do the exact same thing, well I guess that’s their prerogative. Good luck with that. 🙂 Unless a commercial solution somehow jumps way ahead of open source in capability and convenience, I can’t imagine going back. For the entrepreneur or small start-up where capital is a limited commodity and needs to be used as efficiently as possible, open source tech makes much more sense.

When I was only immersed in the MS/ASP.NET world, even simple functionality required a lot of boilerplate code to be written before your app could even begin to work. You could use one of the many O/R frameworks and code generators to overcome some of that, but I’ve yet to see anything as clean and simple as the Ruby/Rails combo. And when that unnecessary complexity isn’t weighing you down, you can move so much faster. Now, when someone asks me if I can do some whiz-bang fancy functionality, I say with confidence, “Sure, we can do that.”

As the enlightened (and rapidly growing) few already know, there is a better way.

Reference Links:

Comments Off on acts_as_awesome

AJAX Spellchecker

How to build an AJAX spellchecker with Ruby on Rails

Word. That is pretty aweseome.

Edit: hahaha, see above. i just went back and read what i posted. oh the irony…

Comments Off on AJAX Spellchecker

RoR Grid Control Part II

My reusable “grid control” with paging and filtering is almost finished. I made some changes to the syntax, and I converted the whole thing to a component, so it can simply be dropped in the components directory of a rails app and used as is.

Defining the columns is very DRY, the minimum thing you have to specify for each column is a title. From this the component will guess the actual database column to be the same (with spaces converted to underscores). If your database column name is different, you can specify that.

I also added some display format options, including “email”, “website” and “money” and “date”. These will make the columns look and behave as you would expect.

Here’s what the code to call it looks like now:

  def my_controller_method
    grid_html = render_component_as_string(:controller => 'controls/grid', :action => 'grid',
      :params => {
        :model => Product,
        :obj => params[:obj],
        :columns => [
          { :title => "Category",
             :db_column => "category_name",
             :filter_type => :select,
             :filter_column => "category_id",
             :filter_options => Category.find(:all, :order => "name ASC").collect {|c| [ c.name, c.id ] }
          },
          { :title => "Name" },
          { :title => "Published",
             :filter_type => :select,
             :filter_options => Product.published_options
          },
          { :title => "Featured",
             :filter_type => :select,
             :filter_options => Product.featured_options
          },
          { :title => "Retail", 
             :db_column => "retail_price", 
             :format => :money, 
             :filter => :false 
          },
          { :title => "Stock", 
             :db_column => "in_stock", 
             :filter => :false }
        ],
        :joins => ["LEFT OUTER JOIN categories ON products.category_id = categories.id"],
        :alias_columns => [["categories.name", "category_name"]],
        :order_by => "category_name, products.name",
        :per_page => 25
      })
    render :text => grid_html, :layout => true
  end

Once I get everything reasonably ironed out and add a few more things like sorting, I plan to open source this code…

Again, here is what mine looks like. This could easily be customized to look any way you want, by simply modifying the view rhtml file:

Comments Off on RoR Grid Control Part II

lighttpd / apache / textdrive speed up!

My websites running on textdrive were running really slow, which was frustrating since I’ve heard all the talk about how lighttpd is supposed to be fast fast fast. I was starting to think it was because my sites are on a shared server with many other websites. To my relief, it wasn’t that at all. After digging around on their forums, I discovered the issue!

To quote user ‘jmoses’:

“The slowness is caused by the Apache proxying. It takes apache forever to lookup the domain name to forward to.

If you change your ProxyPass and ProxyPassReverse from http://<hostname>:<port> to http://localhost:<port> you should see a dramatic speedup. I went from ~5-8s a request, to <1s."

Well, I just made the change, and bam, my sites are way faster.

See the whole thread.

They really need to change this on the textdrive manuals site.

Comments Off on lighttpd / apache / textdrive speed up!

MySpace sold to FOX

Wow. News Corp (owners of FOX) have agreed to purchase Intermix Media (owners of MySpace) for $580 million. Any web developer including myself could have written that website. It’s really not even that great! It could be so much better.

Really need to start thinking about what “next big thing” to create.

Comments Off on MySpace sold to FOX

Ruby on Rails Data Grid Control

As far as I can tell, Ruby on Rails doesn’t have a built in control for displaying tabular data in a grid. Not a rich one anyway, with paging/sorting/filtering capability built-in. I plan on attempting to create one. I’m actually almost there. I’m doing this instead of getting my actual projects done. 🙂

All parameters should have defaults to do the standard thing, with ability to further customize if so desired. Convention over configuration and all that. It should be able to do sorting/paging on columns from associated tables as well. Here’s the proposed syntax for how this might be called in a controller:

a simple example

class AgreementsController < ApplicationController

  def list_simple
    grid_view(Agreement,
      :columns => ['title', 'active', 'amount', 'frequency']
    )
  end

end

a more complex example:

class AgreementsController < ApplicationController

  def list_complex
    grid_view(
      Agreement,
      :columns => [
        "Status",
        "Company Name" => {
          :dbcolumn => "company_name",
          :filter => true
        },
        "Category" => {
          :dbcolumn => "agreement.category_id",
          :filter => true,
          :filter_type => :select,
          :filter_options => Category.find_all.collect {|c| [ c.name, c.id ] }
        },
        "Active" => {
          :dbcolumn => "agreement.active",
          :filter => true,
          :filter_type => :select,
          :filter_options => [["Yes", 1], ["No", 0]]
        }
      ],
      :joins => ["LEFT OUTER JOIN categories ON agreements.category_id = categories.id",
                 "LEFT OUTER JOIN companies ON agreements.company_id = company.id"]
      :alias_columns => [["companies.name", "company_name"],["categories.name", "category_name"]],
      :order_by => "category_name, products.name",
      :per_page => 25
    )
  end

end

The grid_view method will be at the application level, so you can call it from any controller. The presentation (view) will live in one /shared/_grid_view.rhtml partial file and be 100% customizable. And because of that, it doesn’t even have to be a table or look like a grid! The MVC pattern of rails allows this, and would make this far superior and easier to customize than say – the ASP.NET data grid control, in my opinion. Here’s one possible way such a beast might look like:

Comments Off on Ruby on Rails Data Grid Control

Prelude

Just a quick obligatory introduction – I’m Scott Becker, a web designer, developer and entrepreneur by day, musician and party animal by night, living in good old Tampa Florida in the US of A. This is intended to be my tech blog, where I comment on technology and generally get my geek on. On to more useful information…

Comments Off on Prelude