Xirux Nefer
Xirux Nefer

Reputation: 830

Sinatra asset pipeline, can't make it work

I am using Sprockets with Sinatra, as suggested in Sinatra's page docs, but I can't make it work.

When I go to localhost:4567, the page loads correctly but with no styles. If I go to localhost:4567/assets/app.css, I get a not found error. I wonder what I am missing or what is wrong in the way I am using Sprockets?

This is my folder structure:

├── assets
│   ├── css
│   │   ├── app.css
│   │   ├── base.css
│   │   └── normalize.css
├── bin
│   └── app
├── lib
│   ├── app_assets.rb
│   └── main.rb
├── spec
│   ├── spec_helper.rb
│   └── main_spec.rb
├── views
│   └── index.erb
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── .rspec
└── .ruby-version

The contents of app.css are:

//= require normalize
//= require base

The contents of app_assets.rb are:

module AppAssets

  def self.environment root_path
    environment = Sprockets::Environment.new root_path
    environment.append_path './assets/css/'
    environment

    # get assets
    get '/assets/*' do
      env['PATH_INFO'].sub!('/assets', '')
      settings.environment.call(env)
    end
  end

end

The contents of lib/main.rb are:

require 'sinatra'
require 'sprockets'
require 'app_assets'

class Main < Sinatra::Base

  set :views, "#{settings.root}/../views"

  get '/' do
    erb :index
  end

end

The file views/index.erb contains the line:

<link rel="stylesheet" href="assets/app.css">

And the contents of bin/app are:

#!/usr/bin/env ruby

$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')

require 'sinatra'
require 'sprockets'
require 'app_assets'

require 'main'
Main.run!

Which I run typing:

$ bin/app

Any help would be appreciated, I'm sure I made something wrong but I can't see what. Can anybody spot it?

Upvotes: 1

Views: 358

Answers (1)

Kashyap
Kashyap

Reputation: 4796

The app_assets.rb file is the problem here. When you require this file inside another file, the methods you define inside this module are not automatically included. You need to explicitly include AppAssets wherever you need the self.environment method to exist.

The second issue here is that self.environment is not equivalent to settings.environment. If I understand correctly, what you're trying to do is define the asset routing whenever the module gets included. To achieve this one way is to use the included hook for modules. This hook gets run every time you include a module inside a context. If you use that, the code in app_assets.rb turns to:

module AppAssets
  def self.included(klass)
    environment = Sprockets::Environment.new klass.settings.root

    # note the change to path. Since the file where this gets included
    # is inside a sub-folder, we need to traverse to one level above.
    environment.append_path '../assets/css/'
    klass.set :environment, environment

    klass.get '/assets/*' do
      env['PATH_INFO'].sub!('/assets', '')
      klass.settings.environment.call(env)
    end
  end
end

The klass argument to this hook is the class into which this module is included. In our case this is the Sinatra class you've described in main.rb. That file looks like:

class Main < Sinatra::Base
  include AppAssets

  # Same as what you have
end

There's a Sinatra Recipes article about using Sprockets with Sinatra: http://recipes.sinatrarb.com/p/asset_management/sprockets?#article

Upvotes: 2

Related Questions