Friday, November 16, 2012

Custom Domains on Heroku : to zerigo or not to zerigo

So, I'm using Heroku for some smaller projects and while the documentation from Heroku is fairly good, for me it helps to re-process the information for my own understanding.

So, https://devcenter.heroku.com/articles/custom-domains has all the good stuff.

The decision tree:

  • Domain Purchased (do not confuse with DNS)
  • DNS purchased
    • Set the following records:
    • Three (3) A name records with example.com pointing to the 3 IPs listed in the url
    • CNAME record
      • *.example.com pointing to myapp.herokuapp.com
  • DNS not purchased
    • Use Zerigo basic (or the other levels)
    • Zerigo presets the 3 A records
    • You have to add the redirect for www to the domain

It took me a while to really understand how all the elements fit together especially since different providers such as GoDaddy, 1&1, Nettica, dyn.com, Zerigo and the list goes on and on have all completely different interfaces for accomplishing what I needed to do.

This was especially true when setting mail records.

If you are using google apps for your mail, you need to set the 5 MX records to the google app specs which Zerigo so conveniently has a snippet for.

If you need to setup aliases, the mechanism depends on the DNS server and the actual hosting web server.  I use a vps for most of my apps, so I can set the aliases via the cpanel or the terminal.  However, from poking around it looks like some dns services have this ability as well.

Anyhow, this isn't ground breaking information, but it took me a few tries to understand when to use and more importantly not use zerigo with heroku.



Friday, October 19, 2012

What to do when the heroku named app no longer matches the git

Evidently I changed the name of the app on Heroku using the web interface and was no longer able to push my code into heroku.

After poking around the Heroku help files, using the command line tools was ultimately unhelpful to do a rename.

What worked for me was the following:

  1. Verify the name of the git repo for the heroku app under Settings.
  2. Check what git thinks the remote repos are.
$ git remote -v
heroku    git@heroku.com:repo-name.git (fetch)
heroku    git@heroku.com:repo-name.git (push)
origin    git@gitrepo.com:arrow/repo-name.git (fetch)
origin    git@gitrepo.com:arrow/repo-name.git (push)

  1. Edit the .git/config file
 for me the offending portion was in the [heroku] section as it still pointed to the randomly generated name from Heroku instead of the named repo I had renamed it to.

  1 [core]
  2   repositoryformatversion = 0
  3   filemode = true
  4   bare = false
  5   logallrefupdates = true
  6 [remote "origin"]
  7   url = git@gitrepo.com:arrow/repo-name.git
  8   fetch = +refs/heads/*:refs/remotes/origin/*
  9 [remote "heroku"]
 10   url = git@heroku.com:repo-name.git
 11   fetch = +refs/heads/*:refs/remotes/heroku/*


5 minutes fix.  Wish all my bugs were this easy to fix.

Thursday, October 18, 2012

Rails page cache sweeping

Fwoosh fwoosh.  Caching is easy.  Expiring those caches on the other hand can drive you absolutely crazy.

Two things I've learned.

Expiring the cache when your Model is used in a single monolithic pattern is a piece of cake.  Following any standard documentation is easy peasy.

However, one of my Models generates an N number of directories and files dependent on the user's setup.

For example, SiteA can have About Us, FAQ, and Contacts as directories with a number of db served pages for each that are cached.

After many many failed attempts here's what I've learned:

  1. You must setup in config/application.rb the config.active_record.observers = :observer or it will never be found.
  2. Sweepers need to be explicitly told which model to observe "observe :model"
  3. use of {page.cache.directory} needs to be more explicit with  "#{ActionController::Base.page_cache_directory}"
  4. For situations where mass numbers of pages need to be expired simultaneously, be very careful in the use of FileUtils.rm_rf improper usage can blow up your file system.

The last one was a tip learned from the venerable Ryan Bates of Railscasts fame -- of course I'm a subscriber. 

Additionally, when I had a Model that generated a single directory with n number of pages inside such as when caching a paged view, the solution was to do:

FileUtils.rm_rf "#{ActionController::Base.page_cache_directory}/directory"

But I had one model that produced an unknown number of pages.  At first I wanted to hard code the directory structure by copy and pasting the above line changing the final directory.  But that's just stupid.  I've learned that every time I found myself hacking out that pattern that I needed to stop, and rethink the problem.

Since the problem was DB generated, the solution had to be as well.

I simply yanked out all the pathnames from the db and rm -rf each of those.

Model.all.each do |page|
  FileUtils.rm_rf "#{ActionController::Base.page_cache_directory}/#{page.path}"
end




Page Caching and selectively preventing it

I was told this joke from my brother:

There are three problems in programming -- First is counting.  Second is expiring caches.

Me being the idiot that I am asked, what's the third one?

Sigh.

So page caching in Rails is *simple*.
go to Controller, add:

  caches_page :show  


Clearing the cache was an entirely different proposition, but I'll talk about that later.

So here's the scenario.  I have a simple CMS system in place for my project.  it allows the user to create, edit the content for their website.  It also has special editable content pages that are secured using Devise (an awesome gem btw).

The problem was that the secure pages were being cached as well.  Having a html file created in the public directory is a double edged sword.  Having it there meant that the Rails stack (which is slow) never gets called, Yay!  But it also meant that the secure information was now accessible in the public if anyone knew the path to the file.

The only way to authorize those pages was to have it run through the Rails stack.  So I needed to not cache those pages.

On Apidock I found an interesting little snippet using a Proc to prevent caching based on the response type.  Not quite what I needed, but it was on the right track.

1:  # cache the index action  
2:  caches_page :index  
3:  # cache the index action except for JSON requests  
4:  caches_page :index, :if => Proc.new { |c| !c.request.format.json? }  
5:  # don't gzip images  
6:  caches_page :image, :gzip => false  

My standard learning/exploration tool is the debugger tool.  I toss a debugger in the code and see what is available in that specific scope.  Inspecting c was unmanageable as page after page of code screamed past.  What I needed was a way to walk through it in smaller steps.  Google to the rescue!  I found that I could list the params of the object [ c.params].  Ah hah!  Now, I could determine which page was being shown based on the id and from that it was a relatively simple matter of creating an array to check against.

  caches_page :show, :unless => Proc.new { |c|
    secure_pages = Page.find_by_path("secure").pages.map { |page| page.id }
    secure_pages.include? c.params["id"]
  }


To my eyes, it looks simple and clean.  But as a novice, I'm still always worried that I'm doing things the hard way, or worse, the wrong way.


---------------------------

So, I ran into a little glitch.

Having it find_by_path returns a nil when there are no secure pages but a .where('path = ?', 'secure') return an []

The former blows up when I don't have a secure page, the latter gracefully just doesn't find anything.

Gotta love those subtle differences.