Skip to content

Instantly share code, notes, and snippets.

@ahoward
Last active September 12, 2015 01:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ahoward/fc5b9bd1fea380b91c71 to your computer and use it in GitHub Desktop.
Save ahoward/fc5b9bd1fea380b91c71 to your computer and use it in GitHub Desktop.

we use the ro gem

https://github.com/ahoward/ro

a-2:~/git/dojo4/www $ ls public/ro/
chiclets        pages           people          posts



a-2:~/git/dojo4/www $ tree public/ro/posts/|head  -18
public/ro/posts/
├── 28-days-of-kindness
│   ├── assets
│   │   ├── cover.jpg
│   │   ├── donateoldbooks.jpg
│   │   ├── fred-love.jpg
│   │   ├── goodreview.jpg
│   │   ├── hh-5.jpg
│   │   ├── lift.jpg
│   │   ├── mahakala.jpg
│   │   ├── officialdaymaker.jpg
│   │   ├── pancakes.jpg
│   │   ├── paytoll.jpg
│   │   ├── pema.jpg
│   │   └── test.jpeg
│   ├── attributes.yml
│   └── body.md
├── 30-years-ago-i-said-pager-first-but-no-one-listened

we keep our ro stuff out of the way so middleman doesn't process it

### file: config.rb

# ...

#
  Ro.root = File.join(root, 'public', 'ro')

# locally we will serve from public too though
#
  use(Rack::Static, :urls => %w( /ro/ ), :root => "#{ root }/public")
  ignore '/ro/**/**'

# ...

our deployment includes everything in public, including our ro data

### file: ./script/deploy

# ...


  system 'rm -rf deploy'
  system 'mkdir deploy'
  system 'rsync -a build/ deploy/'
  system 'rsync -a public/ro deploy'

# ...

ro gives us really nice jquery-like access to all our data, including template expansion and easy-cheasy relative assets

a-2:~/git/dojo4/www $ ./script/shell 
loaded people from /Users/ahoward/git/dojo4/www/tmp/ro/cache/people.yml
loaded posts from /Users/ahoward/git/dojo4/www/tmp/ro/cache/posts.yml
loaded chiclets from /Users/ahoward/git/dojo4/www/tmp/ro/cache/chiclets.yml
loaded pages from /Users/ahoward/git/dojo4/www/tmp/ro/cache/pages.yml
== Ro reloaded...


www ~> ro.posts.first.class
=> Ro::Node

www ~> ro.posts.first
=> posts/28-days-of-kindness

www ~> ro.posts.first.body.slice(0,100)
=> "<p><a href=\"/team/jessica-watson\">Jessica Watson</a> has put together a super fun project for the mo"

www ~> ro.posts.first.assets.first
=> "cover.jpg"

www ~> ro.posts.first.assets.first.url
=> "/ro/posts/28-days-of-kindness/assets/cover.jpg"

and then, of course, we can do things like this in our config.rb

  #
    if defined?(Post)
    #
      posts     = Post.published
      n         = posts.size
      i         = 0
      per_page  = 6
      pageno    = 0
      num_pages = (posts.size / per_page).ceil

    #
      posts.each_slice(per_page) do |slice|
      #
        locals = {
          :posts     => slice,
          :pageno    => pageno,
          :per_page  => per_page,
          :num_pages => num_pages,
          :next_page => (pageno < num_pages ? pageno + 1 : nil),
          :prev_page => (pageno > 0 ? pageno - 1 : nil)
        }

      #
        proxy "blog/page/#{ pageno }/index.html", "blog/page.html", :ignore => true, :locals => locals

        if pageno.zero?
          proxy "blog/index.html", "blog/page.html", :ignore => true, :locals => locals
        end

        pageno += 1

      #
        posts.each do |post|
          proxy "blog/#{ post.id }/index.html", "blog/post.html", :ignore => true do
            @content = post
            @post = post
          end

          locals = {
            :posts     => [posts],
            :pageno    => pageno,
            :per_page  => per_page,
            :num_pages => num_pages,
            :next_page => (pageno < num_pages ? pageno + 1 : nil),
            :prev_page => (pageno > 0 ? pageno - 1 : nil)
          }

          i += 1
        end
      end
    end

  #
    if defined?(Page)
      pages = Page.published

      pages.each do |page|
        proxy File.join(page.path_info, "index.html"), page.template, :ignore => true, :locals => {:page => page} do
          @content = page
        end
      end
    end

note that we actually wrap the ro nodes, which you can think of as being at the database driver level (but with clever tilt based template expansion) with some lighteight models for convenience

class Post < ::Ro::Model
  extend(Ro::Caching)


  def Post.published
    now = Time.now.utc
    all.
      select{|post| post.status == 'published' and post.published_at and post.published_at <= now}.
        sort{|a, b| b.published_at <=> a.published_at}
  end

  def Post.recent(options = {})
    options      = Map.for(options)
    newer_than   = options[:newer_than] || RECENT_TIME_LINE

    time_ago     = Coerce.time(newer_than).utc
    seconds_diff = Time.now.utc - time_ago
    time_diff    = Post.published.first.published_at - seconds_diff

    Post.published.select{|post| post.published_at.utc >= time_diff}
  end
  RECENT_TIME_LINE = "six months ago"

  def url
    "/blog/#{ slug }"
  end

  def author
    if((author = attributes[:author]))
      Person.detect{|person| person.email == author or person.slug == author}
    end
  end

  def photo
    return @photo if defined?(@photo)

    @photo = (
      re = /\.(gif|jpeg|jpg|png|tif|tiff)$/i

      image = assets.detect{|asset| asset =~ re}

      url_for(image)
    )
  end

  def photo?
    photo
  end

# ....
end

and, of course, all our posts render directly in github because of relative assets:

TL;DR;

i think think middleman blog extension fails badly at adding the correct abstraction layers. to make things really work you need:

  • a good directory layout that will render in github using relative assets
  • a low-level library for manipulating these directories as objects
  • a model layer on top of the low-level library

you might also need

  • some adaptation of your deployment/local rendering

in the end the first three are just what we do in any mvc stack at the db layer:

  • storage
  • db driver
  • models

good design can't stop just because we are in plain-text land

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment