Read my latest article: 8 things I look for in a Ruby on Rails app (posted Thu, 06 Jul 2017 16:59:00 GMT)

Rails Hosting Survey (continued)

Posted by Thu, 15 Jan 2009 22:36:00 GMT

Thanks to everyone has taken the survey and mentioned it on twitter. We just passed 400 people. We have a ways to go before we hit our goal of at least 1500 people surveyed. (if we can get even more than that… great!)

The survey is taking most people less than 5 minutes to complete, so if you haven’t filled it out yet and have experience deploying Ruby on Rails applications, here’s a link. :-)

Any help that you can provide in getting the word out would be greatly appreciated.

Thanks!

Question: Plugins or Gems? (or both?)

Posted by Tue, 13 Jan 2009 18:56:00 GMT

1 comment Latest by Alex Stoneham Tue, 02 Feb 2010 17:20:27 GMT

Our development team likes to extract reusable pieces of code for our projects and have historically used plugins. However, we are finding more and more people releasing these sorts of modules/components/patterns as gems.

Which do you prefer and why?

If you use both, how do you decide to use plugins or gems?

Take the Ruby on Rails Hosting in 2009 Survey

Posted by Tue, 13 Jan 2009 17:40:00 GMT

Calling all Ruby on Rails developers and system administrators.

The team at Planet Argon is hoping to collect some information about how everyone is currently managing the deployment and hosting of their Ruby on Rails applications. We are inviting you all to participate in the Rails Hosting in 2009 survey, which consists of nearly forty questions about you and your Rails hosting experiences. Most people say it is taking less than five (5) minutes to complete it. =)

We will collect responses for the survey until the end of January and will then publish the results (with anonymous raw data) for everyone in the community to share and use.

Our goal is to use this information ourselves to continue to evolve our hosting-related products and deployment services for you. We also want all of our fellow hosting providers and development teams to have access to this information so that they can continue to improve their services. Rails deployment and hosting is getting easier for us all, but we know that there is always room from improvement.

We make an effort to keep our ear close to the ground in the community to listen for trends and problems, but sometimes it’s better to just ask directly.

So, if you have a few minutes to spare, please take the survey!

update: some people mentioned that we should have made some options multi-select. it’s too late to change it without losing submissions. so, for questions like: Monit, God, or Other (and you’re using God and Monit, put that in Other and we’ll track them accordingly)

P.S. Please spread the word about the survey!

Get to know a gem: Ghost

Posted by Mon, 12 Jan 2009 05:18:00 GMT

In my last post, Subdomain accounts with Ruby on Rails explaind, I mentioned that you’d need to modify your /etc/hosts file to use custom subdomains for development/testing. Apparently, there is a much better way to handle this that I was introduced to by Nathan de Vries. Nathan suggests using a gem that I hadn’t heard of before that bares the name of Ghost (view project on github).

Ghost describes itself as…

“A gem that allows you to create, list, and modify local hostnames in 10.5 with easeā€¦”—

If you’ve ever had to modify your /etc/hosts file for anything local, I highly encourage you to check out this shiny gem.

Installing Ghost

Like most gems, you can just install Ghost with the following command.


~ : sudo gem install ghost
Password:
Successfully installed ghost-0.1.2-universal-darwin-9
1 gem installed
Installing ri documentation for ghost-0.1.2-universal-darwin-9...
Installing RDoc documentation for ghost-0.1.2-universal-darwin-9...

Okay, now that Ghost is installed, let’s see what we can do with it.

Using Ghost for local domains/subdomains

Ghost is fairly straight forward. It’s essentially a friendly wrapper for dscl, which is the Directory Service command line utility for Mac OS X. I’ve never played with that directly, but it seems that with Ghost… I shouldn’t need to. :-)

With Ghost, you can add, modify, and delete entries in the Directory Service by issuing any of the following commands. Let’s start out by running ghost to see what we have here.


 ~ : ghost
USAGE: ghost add <hostname> [<ip=127.0.1.1>]
       ghost modify <hostname> <ip>
       ghost delete <hostname>
       ghost list
       ghost empty

Okay, let’s see if there is anything already listed.


   ~ : ghost list
  Listing 0 host(s):

Nope. Let’s test this out. First, we’ll try to ping a domain name that we hope doesn’t exist.


   ~ : ping bigbrown.cow
  ping: cannot resolve bigbrown.cow: Unknown host  

Alright, now we’ll add bigbrown.cow with ghost.


   ~ : ghost add bigbrown.cow
  Password:
    [Adding] bigbrown.cow -> 127.0.0.1

As you can see, it required root credentials to do this as it’s system-wide. Let’s now see if we can talk to bigbrown.cow.


   ~ : ping bigbrown.cow     
  PING bigbrown.cow (127.0.0.1): 56 data bytes
  64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.047 ms
  64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.035 ms
  ^C
  --- bigbrown.cow ping statistics ---
  2 packets transmitted, 2 packets received, 0% packet loss
  round-trip min/avg/max/stddev = 0.035/0.041/0.047/0.006 ms

Excellent! If we run ghost list again, we should see this record.


~ : ghost list
Listing 1 host(s):
  bigbrown.cow -> 127.0.0.1

We can modify the record to a non-localhost IP as well with ghost modify.


   ~ : ghost modify bigbrown.cow 192.168.10.104
    [Modifying] bigbrown.cow -> 192.168.10.104
   ~ : ghost list
  Listing 1 host(s):
    bigbrown.cow -> 192.168.10.104  

I’ll let you play with it yourself as there isn’t much to it. This is a great little addition to my development environment. Thanks to Nathan for pointing it out and to Bodaniel Jeanes for creating this useful gem.

Subdomain accounts with Ruby on Rails explained

Posted by Sun, 11 Jan 2009 22:30:00 GMT

DHH recently posted, How to do Basecamp-style subdomains in Rails on SvN and it just happens that I was implementing some similar stuff this last week for a project we’re developing internally.

In our project, not everything needs to be scoped per-account as we are building a namespace for administrators of the application and also want a promotional site for the product. Three different interfaces, with some overlap between them all.

Let’s walk through a few quick steps that you can follow to setup the two interfaces within the same application.

Suppose that we’re going to build a new web-based product and have the following requirements initially.

  • We need a promotional site for sign-ups, frequently-asked-questions, support requests, etc.
  • When people sign-up for an account, they’ll should have their own unique sub-domain
  • There are two different visual layouts (promotional site and the account)

Note: I use RSpec and am going to skip the TDD process here and let you conquer that for yourself. Am using the default Rails commands in this tutorial.

Account model / Database

We’re going to generate a new model for Account, which will be responsible for scoping sub-domains and individual accounts.

  account-demo : ruby script/generate model Account subdomain:string 
       create  app/models/
       create  test/unit/
       create  test/fixtures/
       create  app/models/account.rb
       create  test/unit/account_test.rb
       create  test/fixtures/accounts.yml
       exists  db/migrate
       create  db/migrate/20090111220627_create_accounts.rb

Great, let’s migrate our application.

   account-demo : rake db:migrate
  ==  CreateAccounts: migrating =================================================
  -- create_table(:accounts)
     -> 0.0045s
  ==  CreateAccounts: migrated (0.0052s) ========================================

Before we get too far, let’s make sure that we’re adding an index on this table for the subdomain, as it’ll improve performance in the database as the subdomain will used in SQL conditions quite often.

account-demo : ruby script/generate migration AddIndexToAccountSubdomain
     exists  db/migrate
     create  db/migrate/20090111221009_add_index_to_account_subdomain.rb

Let’s open up this new migration file and toss in a UNIQUE INDEX on subdomain.

  class AddIndexToAccountSubdomain < ActiveRecord::Migration
    def self.up
      add_index :accounts, :subdomain, :unique => true
    end

    def self.down
      remove_index :accounts, :subdomain
    end
  end  

Okay, let’s migrate this bad boy.

   account-demo : rake db:migrate
  ==  AddIndexToAccountSubdomain: migrating =====================================
  -- add_index(:accounts, :subdomain, {:unique=>true})
     -> 0.0047s
  ==  AddIndexToAccountSubdomain: migrated (0.0050s) ============================  

Great, we’re now ready to move on to the fun stuff.

Let’s open up app/models/account.rb and throw some sugar in it.

Data Validation

Because we’re going to be dealing with subdomains, we need to make sure that we’re only allowing people to sign-up with valid data otherwise, there could be issues. URLs need to fit within certain conventions and we need to make it as graceful as possible for our customers.

Let’s make a quick list of what we need to enforce for the subdomain attributes. This can easily be expanded, but let’s cover the basics.

  • Each account should have a subdomain
  • Each subdomain should be unique within the application
  • A subdomain should be alpha-numeric with no characters or spaces with the exception of a dash (my requirement)
  • A subdomain should be stored as lowercase

So, let’s update the following default Account model….

  class Account < ActiveRecord::Base
  end  

..and add some basic validations.

  class Account < ActiveRecord::Base
    validates_presence_of :subdomain
    validates_format_of :subdomain, :with => /^[A-Za-z0-9-]+$/, :message => 'The subdomain can only contain alphanumeric characters and dashes.', :allow_blank => true
    validates_uniqueness_of :subdomain, :case_sensitive => false

    before_validation :downcase_subdomain

    protected

      def downcase_subdomain
        self.subdomain.downcase! if attribute_present?("subdomain")
      end
  end  

Reserved subdomains

In the project that our team is working on, we wanted to reserve several subdomains so that we could use them later on. We tossed in the following validation as well.

  validates_exclusion_of :subdomain, :in => %w( support blog www billing help api ), :message => "The subdomain <strong>{{value}}</strong> is reserved and unavailable."

This will prevent people from using those when they sign up.

Controller / Handling Requests

Let’s now think about how we’ll handle requests so that we can scope the application to the current account when a subdomain is being referenced in the URL.

For example, let’s say that our application is going to be: http://purplecowapp.com/ [1]

Customers will get to sign-up and reserve http://customer-name.purplecowapp.com/. I want my account subdomain to be green.purplecowapp.com and everything under this subdomain should be related to my instance of the application.

I’ve begun working on my own module, which is inspired mostly by the account_location plugin with some additions to meet some of our product’s requirements.

Here is my attempt to simplify it for you (removed some other project-specific references) and have put this into a Gist for you.

  #
  # Inspired by
  # http://dev.rubyonrails.org/svn/rails/plugins/account_location/lib/account_location.rb
  #
  module SubdomainAccounts
    def self.included( controller )
      controller.helper_method(:account_domain, :account_subdomain, :account_url, :current_account, :default_account_subdomain, :default_account_url)
    end

    protected

      # TODO: need to handle www as well
      def default_account_subdomain
        ''
      end

      def account_url( account_subdomain = default_account_subdomain, use_ssl = request.ssl? )
        http_protocol(use_ssl) + account_host(account_subdomain)
      end

      def account_host( subdomain )
        account_host = ''
        account_host << subdomain + '.'
        account_host << account_domain
      end

      def account_domain
        account_domain = ''
        account_domain << request.domain + request.port_string
      end

      def account_subdomain
        request.subdomains.first || ''
      end

      def default_account_url( use_ssl = request.ssl? )
        http_protocol(use_ssl) + account_domain
      end

      def current_account
        Account.find_by_subdomain(account_subdomain)
      end

      def http_protocol( use_ssl = request.ssl? )
        (use_ssl ? "https://" : "http://")
      end
  end  

View gist here (embed wasn’t working right when I tried)

Just include this into your lib/ directory and require it in config/environment.rb. (if people think it’s worth moving into a plugin, I could do that)

Including AccountSubdomains

In the main application controller (app/controllers/application.rb), just include this submodule.

  class ApplicationController < ActionController::Base
    include SubdomainAccounts

    ...
  end  

Now, we’ll want to add a check to verify that the requested subdomain is a valid account. (our code also checks for status on paid memberships, etc… but I’ll just show a basic version without that)

Let’s add in the following to app/controllers/application.rb. This will only check on the status of the account (via subdomain) if the current subdomain is not the default. For example: purplecowapp.com is just our promotion site, so we won’t look up the account status and/or worry about the subdomain. Otherwise, we’ll check on the status.

  before_filter :check_account_status

  protected
    def check_account_status
      unless account_subdomain == default_account_subdomain
        # TODO: this is where we could check to see if the account is active as well (paid, etc...)
        redirect_to default_account_url if current_account.nil? 
      end
    end  

Current Account meets Project model

When requests are made to an account’s subdomain, we want to be able to scope our controller actions.

WARNING: I’m going to gloss over the following steps because this is just standard Rails development stuff and I want to focus on how to scope your Rails code to account subdomains.

I’ll just say that this product gives each account many projects to do stuff within. I’ll assume that you’ll know how to handle all that and we’ll assume you have a Project model already.

What you will need is to add a foreign key to your table (projects in this example) that references Account. So, make sure that your model has an account_id attribute with and that the database table column has an INDEX.

We’ll add our associations in the models so that they can reference each other.

  # app/models/account.rb
  class Account < ActiveRecord::Base
    has_many :projects
    # ...
  end

  # app/models/project.rb
  class Project < ActiveRecord::Base
    belongs_to :account
    # ...
  end  

Okay great… back to our controllers. The SubdomainAccounts module provides you with the current_account variable, which you can use within your controllers/views. This allows us to do the following in our controllers. For example, if we had a ProjectsController.

  class ProjectsController < ApplicationController
    def index
      @projects = current_account.projects.find(:all)
    end

    def new
      @project = current_account.projects.new
    end 

    def show
      @project = current_account.projects.find(params[:id])
    end

    # ...
  end

See, this wasn’t so hard, was it?

Handling layouts

I wanted to highlight one other thing here because I suspect that most projects that fit this will likely need a promotional/resource site where people will sign-up from. In our application, we have two application layouts. One for the main application that customers will interact with via their subdomain and the promotional site layout.

The default layout is just app/views/layouts/application.html.erb and we have our promotional site layout at app/views/layouts/promo_site.html.erb. A few of our controllers are specifically for the promotional site while the rest are for the application itself and in some cases, there is some overlap down to individual action within a controller.

What we did was add a few more before filters to our application controller to a) define the proper layout to render, and b) skip login_required on the promo site.

To have the proper layout get rendered, we’re just checking whether the current request was made to the promotional site or not.

class ApplicationController < ActionController::Base
  # ...
  layout :current_layout_name # sets the proper layout, for promo_site or application

protected

  def promo_site?
    account_subdomain == default_account_subdomain
  end

  def current_layout_name
    promo_site? ? 'promo_site' : 'application'
  end

  # ...
end

Our application is using Restful Authentication and we just want to check to see if the current request is made to the promotional site or not. If it is, we’ll skip the login_required filter. Let’s assume that you have the following before_filter set.

class ApplicationController < ActionController::Base
  # ...
  before_filter :login_required

We’ll just change this to:

class ApplicationController < ActionController::Base
  # ..
  before_filter :check_if_login_is_required 

  protected
    def promo_site?
      account_subdomain == default_account_subdomain
    end

    def current_layout_name
      promo_site? ? 'promo_site' : 'application'
    end    

    def check_if_login_is_required
      login_required unless promo_site?
    end

    # ...

There we go. We can now render the proper layout given the request and only handle authentication when necessary.

Development with account subdomains

When you begin developing an application like this, you need to move beyond using http://locahost:3000 as we need to be able to develop and test with subdomains. You can open up your /etc/hosts (within a Unix-based O/S) file and add the following.


127.0.0.1 purplecowapp.dev
127.0.0.1 green.purplecowapp.dev
127.0.0.1 sample.purplecowapp.dev
127.0.0.1 planetargon.purplecowapp.dev
127.0.0.1 lollipops.purplecowapp.dev
127.0.0.1 help.purplecowapp.dev
127.0.0.1 support.purplecowapp.dev

After you edit that file (with root permissions), you can flush your dns cache with dscacheutil -flushcache (Mac OS X). This will let you make requests to http://purplecowapp.dev:3000/ and http://green.purplecowapp.dev:3000. This is a convention that our team has begun using for our own projects (TLD ending in .dev). It’s important to remember that the subdomain must be specified here in order to work for local requests. Unfortunately, hosts files don’t support wildcards (’*’).

Update

You can also use Ghost, which is a gem for managing DNS entries locally with Mac OS X. Read Get to know a gem: Ghost

Summary

I know that I glossed over some sections, but was hoping that the code itself would be the most beneficial for you. Feel free to leave any questions and/or provide some feedback on our approach. Perhaps you have some suggestions that I could incorporate into this so that we can improve on this pattern.

1 yeah, I’ve been reading more Seth Godin recently…

Rails 3 and Merb

Posted by Tue, 23 Dec 2008 21:55:00 GMT

So… Rails and Merb are going to be merged into Rails 3. (link)

Has hell frozen over?

(it has in Portland the last week)

I’m curious about how the revised core team will incorporate the library-agnostic view points into Rails without increasing the complexity for configuration. For example, being able to use a different ORM is great, but at the same time, one of the things that I have really liked about Ruby on Rails was that it did make decisions ahead of time for you. Conventions over Configuration and all that jazz. While they intend to keep these defaults, I really wonder how much more configuration will be involved. Be that as it may, Rails and Merb are run by some of the best developers I’ve ever known… so I am sure these decisions will not be made without some deep consideration.

Rails application don’t all look and smell the same, but it’s nice to know that there is consistency across all of our client applications. What I’m concerned about (from an efficiency standpoint) is that this could lead to project-diversity at the cost of experimenting. Pre-Rails, the development teams that I was a part of was constantly trying out new libraries from client project to project, but this came at a huge cost. We weren’t able to leverage our experience with previous projects like our team does with Ruby on Rails currently. (hell, I even helped write two different ORMs in the two years before Rails for PHP… and still wasn’t satisfied)

But, this isn’t so much a technical problem as much as a people problem. The thing is… is that Rails helped solve a people problem with a technical answer. Having testing, consistency, and other best practices built-in did the world a huge favor. ...and all it took was someone like DHH to throw his opinion out there and stick to it. It took me nearly a full year to really embrace a lot of these conventions, but in the end.. it paid off.

While I do feel that it’s in developers best interests to try out new approaches, I just don’t think it should be on your clients dime. This was part of the reason why I quit my last job to start Planet Argon full-time. I really wanted to get away from that cycle.

Since we (Planet Argon) adopted Ruby on Rails four years ago, we’ve been able to build off of every project we had worked on before. We since adopted things like RSpec and JQuery, but our team decided on these changes after someone took the initiative to experiment with these on internal and personal projects. Having this foundation has freed up a lot of our time to focus on other issues as a team, like Interaction Design, Usability, and client collaboration.

As far as Merb itself, I honestly haven’t tried to do anything with it since about 0.2/0.3. I gave up quickly though because the documentation didn’t help me get anywhere and my time is valuable. I’ve since seen that documentation has improved drastically, but I haven’t been able to prioritize the time needed to really play with it. With Merb being merged into Rails 3, it means that I really should spend more time exploring it as we might be able to leverage some of it’s benefits without as much of an investment.

Much of the lack of great interest in Merb was because I felt Rails had consistently provided our team with a solid foundation for a majority of our internal and client applications. The old saying, “if it ain’t broke, don’t fix it.” Not to say that others haven’t expressed a lot of excitement about Merb and it’s benefits, I just didn’t see there being enough of a productivity gain to warrant the time investment required to really learn and use a new framework… and the one thing that I have had trouble with was that it didn’t sound like Merb encouraged a default set of libraries. I could be totally wrong, but that’s been the perception I’ve had based on how it was branded.

But… the best part about this for you, me, and the Rails community? Is that I don’t need to register robbyonmerb.com anytime soon. ;-)

I hope that you’re all having a great end to 2008 and am excited to see all the energy in the Ruby/Rails/Merb community. I suspect that between these two (now-merged) teams, we’ll have an even better platform to develop web applications on. I believe this is great news and I’m all in favor of seeing the Ruby community conquer these challenges that lay ahead.

Anyhow, I’m just thinking out loud. What are your thoughts?

Older posts: 1 ... 3 4 5 6 7 ... 25