I read an article last month on Adding static pages to your Rails app and I’ve been meaning to write up how we do the same thing at Clockwise.MD. In fact, it’s been a very long time since I’ve written on my blog.
It’s funny that I’m finding time to write in the middle of a hackathon at 48-in-48. After joining three teams, it seems I’ve completed everything on my list and I’m waiting on Stephanie to wrap up. So that must mean it’s a great time to start writing again and create a blog entry! Hopefully I can write up something about this experience when I’m done.
Back to static pages for your web application.
The benefit of splitting out your main app from your static marketing pages is creating an enviornment the best suits your authors/editors, designers and engineers. Take Clockwise.MD for instance, we have a well defined SDLC process for making changes to the web application. It includes testing, peer review, approvals and carefully selecting which memoral quote, interesting fact or animated GIF to add to your PR approval. The flip side is our head of marketing shouldn’t need an extensive PR process to add a new pet to our Team page.
Similar to the earlier article I mentioned, we needed to load static pages up for our app but we don’t want to host those files on our actual web server. Instead, we created our web site using Jekyll. The resulting static site is uploaded to S3 with a CDN in front to make the site load fast. Then any files we don’t want to serve up from our web application, we proxy down from our CDN and send it to the requestor. We also cache it so that it’s super fast.
class PagesController < ApplicationController def www_base expires_in 1.hour, public: true response = s3_pages(request.path) render status: response.code, inline: response.body end private def s3_pages(path) uri = URI('https://your-s3-or-cdn-site.com' + adjusted_path(path)) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.ciphers = 'TLSv1.2:!aNULL:!eNULL' http.ssl_version = :TLSv1_2 http.verify_mode = OpenSSL::SSL::VERIFY_PEER request = Net::HTTP::Get.new(uri.request_uri) http.request(request) end end
Then at the bottom of your
routes.rb file, add:
# external pages root to: 'pages#www_base' get '*unmatched_route', to: 'pages#www_base'
Now your home page will hit
pages#www_base. Additionally, any page that’s
not already specified earlier on your web application’s routes will be routed
to your static site. If a user hits
/products, then that same request will
get served up from your static site. If a user hits
/foo/bar and that path
doesn’t exist, then they’ll even get your static site’s 404 page and
will send back the correct 404 status code as well.
To make things easier for your marketing folks, let them edit pages using Github’s online editors then setup a CI process to rebuild and redeploy the static site on every commit. Instant updates without having to the install the Wordpress malware.
It’s apparently a known issue to have slow Rails apps under Docker in development due to folder sharing in VirtualBox.
There are many suggestions as to how to fix this, so I tried a few.
- Sharing Host Volumes with Docker Containers is a good overview of multiple things to try by Oliver Günther.
- I started with docker-osx-dev but received rsync errors after running it for a few minutes.
- Switched to VMWare Fusion and that didn’t help so maybe it’s not VirtualBox file sharing.
- Tried docker-rysnc under Fusion but it gave me:
rsync: connection unexpectedly closed (0 bytes received so far) [sender] rsync error: error in rsync protocol data stream (code 12) at /BuildRoot/Library/Caches/com.apple.xbs/Sources/rsync/rsync-47/rsync/io.c(453) [sender=2.6.9] error: exit status 12 Watching for file changes ...
Maybe this magic bullet shit won’t work, let’s try using an image as part of compose so that we’re more explicit about this syncing process.
- Looks like docker-unison works to sync files and it syncs them quickly but the application still takes 20 seconds to load a simple page.
- dinghy created a VM as a wrapper to docker-machine and no luck with that too.
Other NFS scripting options:
- Boot2docker: Using nfs instead of vboxsf to mount /Users
- How Blackfire leverages Docker under the File Sharing section
- boot2docker-nfs.rb is a simple gist working with VirtualBox
Since I have Fusion installed though, this seems like a lot of work to just get a basic Rails app to run.
- Use Docker for everything not Rails or Guard. Postgres and workers seem fast enough.
- Run Rails locally with your standard
gem install bundler; bundle.
- Leave the app running in Docker Compose if I need to check out anything in Linux but avoid it at all costs.
I’ve got too much real work to do.
So I finally got around to upgrading to Mountain Lion.
My first stop was to follow Thoughtbot’s The Hitchhiker’s Guide to Riding a Mountain Lion.
- Get Xcode and Command Line Tools installed
- Fix homebrew permissions with
sudo chown -R $USER /usr/local
- Update homebrew with
- Install gcc 4.2 to install older rubies with
brew tap homebrew/dupes; brew install autoconf automake apple-gcc42
- Install X11 support with XQuartz
TL;DR for installing Ruby 1.8.7
$ export CPPFLAGS=-I/opt/X11/include $ CC=/usr/local/bin/gcc-4.2 rvm install 1.8.7
My little story
Initially I didn’t want to install X11 since I didn’t think I needed it.
Then I tried to use
vim (not MacVim, I just wanted to edit a file
quickly) and received:
$ vim Vim: Caught deadly signal SEGV Vim: Finished.  42573 segmentation fault vim
What version of vim?
$ vim --version VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Jun 20 2012 13:16:02) Compiled by email@example.com /snip/
It seems that if you
brew install vim, it still breaks:
$ /usr/local/bin/vim --version VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Oct 15 2012 18:02:59) MacOS X (unix) version Included patches: 1-687 Compiled by databyte@nyx
Turns out that I need ruby-1.8.7 due to a compatibility in
Janus, which was outdated. The
quick fix was upgrading Janus by running
~/.vim but I
might as well compile ruby-1.8.7 since I bothered to install gcc 4.2.
Let’s try to install ruby-1.8.7 with rvm:
$ rvm install ruby-1.8.7-p370 No binary rubies available for: osx/10.8/x86_64/ruby-1.8.7-p370. Continuing with compilation. Please read 'rvm mount' to get more information on binary rubies. Installing Ruby from source to: /Users/databyte/.rvm/rubies/ruby-1.8.7-p370, this may take a while depending on your cpu(s)... ruby-1.8.7-p370 - #downloading ruby-1.8.7-p370, this may take a while depending on your connection... ruby-1.8.7-p370 - #extracting ruby-1.8.7-p370 to /Users/databyte/.rvm/src/ruby-1.8.7-p370 ruby-1.8.7-p370 - #extracted to /Users/databyte/.rvm/src/ruby-1.8.7-p370 Applying patch /Users/databyte/.rvm/patches/ruby/1.8.7/stdout-rouge-fix.patch Applying patch /Users/databyte/.rvm/patches/ruby/1.8.7/no_sslv2.diff ruby-1.8.7-p370 - #configuring ruby-1.8.7-p370 - #compiling Error running 'make', please read /Users/databyte/.rvm/log/ruby-1.8.7-p370/make.log There has been an error while running make. Halting the installation. Please be aware that you just installed a ruby that requires 2 patches just to be compiled on up to date linux system. This may have known and unaccounted for security vulnerabilities. Please consider upgrading to Ruby 1.9.3-286 which will have all of the latest security patches.
Turns out in the logs that:
/usr/include/tk.h:78:23: error: X11/Xlib.h: No such file or directory
Hence the need for XQuartz. Now let’s install XQuartz and then reinstall ruby but also explicity set X11’s include path:
$ export CPPFLAGS=-I/opt/X11/include $ CC=/usr/local/bin/gcc-4.2 rvm reinstall 1.8.7 Removing /Users/databyte/.rvm/src/ruby-1.8.7-p370... /Users/databyte/.rvm/rubies/ruby-1.8.7-p370 has already been removed. No binary rubies available for: osx/10.8/x86_64/ruby-1.8.7-p370. Continuing with compilation. Please read 'rvm mount' to get more information on binary rubies. Installing Ruby from source to: /Users/databyte/.rvm/rubies/ruby-1.8.7-p370, this may take a while depending on your cpu(s)... ruby-1.8.7-p370 - #downloading ruby-1.8.7-p370, this may take a while depending on your connection... ruby-1.8.7-p370 - #extracting ruby-1.8.7-p370 to /Users/databyte/.rvm/src/ruby-1.8.7-p370 ruby-1.8.7-p370 - #extracted to /Users/databyte/.rvm/src/ruby-1.8.7-p370 Applying patch /Users/databyte/.rvm/patches/ruby/1.8.7/stdout-rouge-fix.patch Applying patch /Users/databyte/.rvm/patches/ruby/1.8.7/no_sslv2.diff ruby-1.8.7-p370 - #configuring ruby-1.8.7-p370 - #compiling ruby-1.8.7-p370 - #installing Removing old Rubygems files... Installing rubygems-1.8.24 for ruby-1.8.7-p370 ... Installation of rubygems completed successfully. Saving wrappers to '/Users/databyte/.rvm/bin'. ruby-1.8.7-p370 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake). ruby-1.8.7-p370 - #importing default gemsets (/Users/databyte/.rvm/gemsets/) Install of ruby-1.8.7-p370 - #complete Please be aware that you just installed a ruby that requires 2 patches just to be compiled on up to date linux system. This may have known and unaccounted for security vulnerabilities. Please consider upgrading to Ruby 1.9.3-286 which will have all of the latest security patches. Making gemset ruby-1.8.7-p370 pristine. Making gemset ruby-1.8.7-p370@global pristine.
Well that works and now I have ruby 1.8.7. Let’s try out vim again:
$ rvm use 1.8.7 $ /usr/local/bin/vim
Yep, that works too!
$ vim --version VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Oct 15 2012 18:08:38) MacOS X (unix) version Included patches: 1-687 Compiled by databyte@ Huge version without GUI. Features included (+) or not (-): +arabic +autocmd -balloon_eval -browse ++builtin_terms +byte_offset +cindent -clientserver +clipboard +cmdline_compl +cmdline_hist +cmdline_info +comments +conceal +cryptv +cscope +cursorbind +cursorshape +dialog_con +diff +digraphs -dnd -ebcdic +emacs_tags +eval +ex_extra +extra_search +farsi +file_in_path +find_in_path +float +folding -footer +fork() -gettext -hangul_input +iconv +insert_expand +jumplist +keymap +langmap +libcall +linebreak +lispindent +listcmds +localmap -lua +menu +mksession +modify_fname +mouse -mouseshape +mouse_dec -mouse_gpm -mouse_jsbterm +mouse_netterm -mouse_sysmouse +mouse_xterm +mouse_urxvt +mouse_sgr +multi_byte +multi_lang -mzscheme +netbeans_intg +path_extra -perl +persistent_undo +postscript +printer +profile +python -python3 +quickfix +reltime +rightleft +ruby +scrollbind +signs +smartindent -sniff +startuptime +statusline -sun_workshop +syntax +tag_binary +tag_old_static -tag_any_white -tcl +terminfo +termresponse +textobjects +title -toolbar +user_commands +vertsplit +virtualedit +visual +visualextra +viminfo +vreplace +wildignore +wildmenu +windows +writebackup -X11 -xfontset -xim -xsmp -xterm_clipboard -xterm_save system vimrc file: "$VIM/vimrc" user vimrc file: "$HOME/.vimrc" user exrc file: "$HOME/.exrc" fall-back for $VIM: "/usr/local/share/vim" Compilation: cc -c -I. -Iproto -DHAVE_CONFIG_H -DMACOS_X_UNIX -no-cpp-precomp -g -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 Linking: cc -L. -L/usr/local/lib -o vim -lm -lncurses -liconv -framework Cocoa -framework Python -lruby -lobjc