Sprockets asset_path error on Sinatra - sinatra

I'm working on a Sinatra project that's using Sprockets. When I add a new stylesheet in the assets folder, I get this error:
Error compiling CSS asset
Sprockets::NotImplementedError: Custom asset_path helper is not
implemented
Extend your environment context with a custom method.
environment.context_class.class_eval do
def asset_path(path, options = {})
end
end
What am I doing wrong?

I followed the error message's suggestion: I defined the asset_path method in config.ru:
environment.context_class.class_eval do
def asset_path(path, options = {})
"/assets/#{path}"
end
end
I'm still not sure why this is needed, but it made the error go away.

To fix the same error in Padrino, I defined this method in my app.rb and changed environment to assets:
assets.context_class.class_eval do
def asset_path(path, options = {})
"/assets/#{path}"
end
end

Has already proposed, you need to define the method asset_path for your environment.
This method is used by helpers like image_url, ... commonly used in CSS files. You might want to make it a little different depending on the options[:type] then.
Example:
environment.context_class.class_eval do
def asset_path(path, options = {})
if type = options[:type]
"/assets/#{type.to_s.pluralize}/#{path}"
else
"/assets/#{path}"
end
end
end
Doing so mill make the asset_url to return /assets/path/to/your/file while the image_url helper will return /assets/images/path/to/your/file

Don't we need an instance of sprockets call?
For a Rails app that I'm playing with I used this in the config.ru to get rid of the error.
map '/assets' do
environment = Sprockets::Environment.new
environment.context_class.class_eval do
def asset_path(path, options = {})
"app/assets/#{path}"
end
end
environment.append_path 'app/assets/javascripts'
environment.append_path 'app/assets/stylesheets'
run environment
end

Related

In Sinatra, is there a way to forward a request to another app?

I'm trying to do something like this:
class Dispatcher < Sinatra::Base
def initialize
#foo = Foo.new
end
get '/foo/*' do
#foo.call(params[:splat])
end
end
So that URL /foo/abc/def?xxx=yyy would be like calling the Foo app with /abc/def?xxx=yyy.
This seems like it should be easy, but I haven't seen any example of it.
I ended up doing this in a Rack config.ru file:
map "/abc" do
run Foo.new('abc')
end
map "/def" do
run Foo.new('def')
end
Not exactly what I wanted, but saves me from modifying the underlying app.
I'm not sure why you would use Sinatra for that. If I understand you right, and you use Apache with Proxy-Rewrite rules you just do:
The .htaccess file
RewriteEngine On
RewriteRule ^foo/(.*) http://localhost:61000/$1 [P]
So all your domain.tdl/foo get redirected to your local running app athttp://localhost:61000/ with all Post and Get Parameters.

How do I use Sprockets with Sinatra without a rackup file?

I'm writing a library that has an embedded Sinatra app launched via Thor. I want to mount instances of Sprockets::Environment at /css and /js and have the main app mapped to /. This would be easy using Rack::URLMap in a config.ru file, but in this case there isn't one because I'm starting the Sinatra app programmatically with Sinatra::Application.run!. How can I achieve this?
Actually, this is not that hard. All you need to do is assign an instance of Sprockets::Environment to a Sinatra configuration variable and define some paths to look up the assets you're interested in.
Here's a basic example:
require "sass"
require "haml"
require "erubis"
require "sinatra"
require "sprockets"
set :assets, Sprockets::Environment.new
# Configure sprockets
settings.assets.append_path "app/javascripts"
settings.assets.append_path "app/stylesheets"
# For compressed JS and CSS output
require "yui/compressor"
settings.assets.js_compressor = YUI::JavaScriptCompressor.new
settings.assets.css_compressor = YUI::CssCompressor.new
get "/" do
haml :index
end
get "/javascripts/:file.js" do
content_type "application/javascript"
settings.assets["#{params[:file]}.js"]
end
get "/stylesheets/:file.css" do
content_type "text/css"
settings.assets["#{params[:file]}.css"]
end
Happy sprocketing!
I ended up doing it by writing a custom middleware with some of the functionality from Rack::URLMap. It looks roughly like this:
require "sprockets"
require "sinatra/base"
class SprocketsMiddleware
attr_reader :app, :prefix, :sprockets
def initialize(app, prefix)
#app = app
#prefix = prefix
#sprockets = Sprockets::Environment.new
yield sprockets if block_given?
end
def call(env)
path_info = env["PATH_INFO"]
if path_info =~ prefix
env["PATH_INFO"].sub!(prefix, "")
sprockets.call(env)
else
app.call(env)
end
ensure
env["PATH_INFO"] = path_info
end
end
class App < Sinatra::Base
use SprocketsMiddleware, %r{/assets} do |env|
env.append_path "assets/css"
env.append_path "assets/js"
end
end
App.run!
Here is how I integrated Sprockets into Sinatra with Rails-like directory layout, helpers and minification for JS and CSS.
I chose to write a Sinatra extension. This extension encapsulates the configuration of sprockets (paths, minification, helpers) and can be registered by the application.
module Sinatra
module Assets
extend Sinatra::Extension
configure do
set :assets, Sprockets::Environment.new(root).tap { |assets|
%w(assets vendor/assets).each do |base|
%w(images javascripts stylesheets).each do |type|
assets.append_path File.join(base, type)
end
end
if production?
assets.js_compressor = Closure::Compiler.new
assets.css_compressor = YUI::CssCompressor.new
uid = Digest::MD5.hexdigest(File.dirname(__FILE__))[0,8]
assets.cache = Sprockets::Cache::FileStore.new("/tmp/sinatra-#{uid}")
else
assets.cache = nil
end
}
end
get "/assets/*" do
env["PATH_INFO"].sub!(%r{^/assets}, "")
expires Time.now + (365*24*60*60) if settings.production?
settings.assets.call(env)
end
helpers do
include Sprockets::Helpers
Sprockets::Helpers.configure do |config|
config.expand = development?
config.digest = production?
end
def assets_environment
settings.assets
end
end
end
end
Using the extension in your application is simple:
class App < Sinatra::Base
register Sinatra::Assets
# ...
end
Assets can be placed in assets, or vendor/assets. For example vendor/assets/jquery.js can be referenced by logical name, i.e. http://localhost/assets/jquery.js.
In the example above I'm using sprockets-helpers which provides helpers such as javascript_tag. The configuration given above assumes that in development, you want to expand assets required by the referenced asset (resulting in multiple tags per asset).

Ember.js asset pipeline “Unable to find template”

I am having trouble getting my handlebars templates accessible in rails 3.1. I have the following controller:
Lead.Controllers.UrlSearch = Ember.Object.extend
init: ->
view = Ember.View.create
controller: #
urlSearchBinding: 'controller.url_search'
templateName: 'app/templates/url_search/show'
On the rails side of things, I have a the following initialisation script in config/initializers/sprockets.rb
require 'sprockets/ember_handlebars'
Rails.application.assets.register_engine 'hjs', EmberHandlebars
My EmberHandleBars looks like this:
require 'tilt'
require 'json'
class EmberHandlebars < Tilt::Template
def self.default_mime_type
"application/javascript"
end
def prepare
end
def evaluate(scope, locals, &block)
"Ember.TEMPLATES['#{scope.logical_path}'] = Ember.Handlebars.compile(#{data.to_json})"
end
end
Finally, the template is located in:
app/assets/javascripts/app/templates/url_search/show.jst.hjs
In there error console, I get this 404 resource error:
GET
http://localhost:3000/assets/app/templates/url_search/show.hjs.js?body=1
404 (Not Found)
and also
Error: - Unable to find template
"app/templates/url_search/show".
I am confused why it is looking for an hjs.js file when I have specified otherwise and why it cannot find the template.
Can anyone see what I am doing wrong?
Changing the file extensions form .jst.hjs to just .hjs fixed the problem.

How Does the ActiveResource Get Call show find(:first) or find(:last) requests?

I am developing a Sinatra server that can accept calls from ActiveResource, but can"t determine how to identify Get calls specificying :first or :last.
In Rails 3
User.find(:first) => localhost.com/user.xml
User.find(:last) => localhost.com/user.xml
This works exactly as it should according to the examples in the ActiveResource documentation.
It is clear what path they request (the same one), but it is not clear what happens to the :first or :last elements. I can not find them in the request object on the Sinatra server. Does anyone know what happened to those references?
Thanks for your help.
Code from ActiveResource library
def find(*arguments)
scope = arguments.slice!(0)
options = arguments.slice!(0) || {}
case scope
when :all then find_every(options)
when :first then find_every(options).first
when :last then find_every(options).last
when :one then find_one(options)
else find_single(scope, options)
end
end
last and first just methods from Enumerable module

Testing view helpers

I'm currently working on a Rails plugin used for generating iPhone specific HTML meta-tags. I'm trying to use ActionView::TestCase for unit tests but keep getting the same error. See file contents and error below. Any ideas or help would be much appreciated.
test_helper.rb
require 'rubygems'
require 'test/unit'
require 'active_support'
require 'action_view'
require File.join(File.dirname(__FILE__), '..', 'lib', 'iphone_helper')
iphone_test_helper.rb
require 'test_helper'
class IphoneHelperTest < ActionView::TestCase
test 'br' do
tag = tag('br')
assert_tag_in tag, '<br />'
end
end
error
RuntimeError: In order to use #url_for, you must include routing helpers explicitly. For instance, `include Rails.application.routes.url_helpers
Awful and hacky workaround that worked for me (since I am working on a gem and not in a full rails environment):
require 'ostruct'
module ActionController::UrlFor
def _routes
helpers = OpenStruct.new
helpers.url_helpers = Module.new
helpers
end
end
Did you try to include the respective Module in an old-fashioned way?:
include ActionDispatch::Routing::RouteSet
If a NameError is raised telling you that ActionDispatch is unknown you might have to require 'action_dispatch'.
Maybe a stupid question, but is the fact that the class name and the file name don't match possibly a problem (IphoneHelperTest vs. iphone_test_helper.rb)? Sometimes that leads to classes not being loaded.