I have a ruby sinatra app that uses haml. I use layout.haml for a common menu across all pages. Let's say I have login.haml, main.haml and reports.haml. I only want to use layout.haml for login.haml and main.haml. How do I exclude layout.haml from reports.haml? thanks
Two (and a somewhat) ways:
Global
class MyApp < Sinatra::Base
set :haml, :layout => false
get '/reports' do
haml :reports
end
end
Blacklisting
If the number of routes that do not require layouts are less, then this is a pattern:
class MyApp < Sinatra::Base
get '/reports' do
haml :reports, :layout => false
end
end
Whitelisting
If the routes that don't need a layout.haml file are more, however, Sinatra does not seem to support overriding the global declaration of set :haml, :layout => false. I've taken the liberty to open up an issue for this feature as it seems reasonable enough (Hope you won't mind).
Related
The gist
Is it possible to bake feature-toggle-like functionality into a Sinatra application?
A bit about feature toggles, just in-case ;)
Back story
I've set up a modular Sinatra project, and I tend to implement a GET/POST/PUT/DELETE endpoint for all my resources; it makes it easier to test the app and manipulate the data while in development.
Problem
When I go into production I don't want the unneeded endpoints to exist (e.g DELETE '/users').
Question
Can I annotate the methods with some kind of a :development flag, or maybe intercept the request in a before block? Would you do this using a helper? I'm not sure if I'm heading down the right path here, I'm probably over complicating it(?)
How would one go about this?
If you've done something like this it would be great if you can share your findings with the nation.
You can use the current environment to decide whether you define an action. For example:
class MyApp < Sinatra::Application
if settings.development?
get '/admin' do
'VIPs only'
end
end
end
If you have a lot to toggle, you might want to isolate them in one file that you can decide to require or not:
# routes/init.rb
require_relative 'main'
require_relative 'debug' if settings.development?
# routes/main.rb
class MyApp < Sinatra::Application
get '/' do
'Hello!'
end
end
# routes/debug.rb
class MyApp < Sinatra::Application
get '/admin' do
'VIPs only'
end
end
Or if you want to list your development-only paths in one place, here's a filter version:
class MyApp < Sinatra::Application
DEVELOPMENT_PATHS = %w[
/admin
]
before do
unless settings.development? || !DEVELOPMENT_PATHS.include?(request.path)
halt 404
end
end
end
Then you could also build some decorator-like methods that add to the list:
class MyApp < Sinatra::Application
def self.development_only(path)
DEVELOPMENT_PATHS << path
end
get '/admin' do
'VIPs only'
end
development_only '/admin
end
In general, I'd recommend caution when introducing significant differences between the code that runs in development vs. production. Inevitably, the dev code is either left untested or becomes cumbersome to maintain properly. In this case, there's the danger that you miss a route you intended to hide and it becomes available to everyone in production. I'd tend towards not having these routes at all and manipulating my dev environment from a console, or going all the way to the other end and building fully-tested and production-ready user permissions with something like sinatra-authentication.
I want to do essentially the same thing: How do I automatically generate static HTML from HAML with Sinatra or Padrino?
This answer didn't quite go far enough for me. After I have caching setup, how would I send a particular haml file to the client?
E.g.
get '/foo', :cache => true do
expires 30
haml '/templates/foo.haml'
end
If you want to serve any file in templates you could do something like:
get '/*', :cache => true do |name|
expires 30
haml "/templates/#{name}.haml"
end
and they will all be cached.
this question was asked before, but the answer was for ruby on rails 3.0+
Basically I want to put my wiki pages (ie .md pages) inside my public folder in my 2.3.5 ruby on rails project. and I want users to access the home of the wiki page when they type mysite.com/wiki (ie this would map to /public/wiki/home.md)..
how do I do this in ruby on rails 2.3.5? (the routing documentation online wasn't very informative)
Also in general if for some reason I'm stuck with an RoR 2.3.5 project.. where do I go for documentation? It seems the official documentation only pertains to the latest RoR version (ie 3+)
I presume that you want the Markdown to be rendered. If you simply serve it up from your public directory, then Rails won't render it.
What you could do is an a new controller, say WikiController, which could render markdown files that you store somewhere like lib/wiki. I haven't tested any of this directly, so you should take it only as a guide, but it should work okay.
The controller might look like something like this:
# app/controllers/wiki_controller.rb
class WikiController < ApplicationController
def show
page = File.open(File.join(Rails.root, 'lib', 'wiki', "#{params[:page_id]}.md"), 'r') { |f| f.read }
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true)
render :html => markdown.render(File.join(Rails.root, 'lib', 'wiki', "#{params[:page_id]}.md"))
end
end
And you could add a route like this:
# config/routes.rb
map.connect 'wiki', :controller => 'wiki', :action => 'show', :page_id => 'home'
map.connect 'wiki/*page_id', :controller => 'wiki', :action => 'show', :as => :wiki
The first route handles your special case (home.md) and the second will allow you to structure your wiki however you like (including placing files in subdirectories, etc). Linking to /wiki/help/getting_started will try to render the file lib/wiki/help/getting_started.md.
You also have a link helper method, so that if you need to link to a wiki page from within your app you can call wiki_path(:page_id => 'help/getting_started').
This solution assumes you're using RedCarpet for Markdown rendering, however you could switch up any renderer you like.
sinatra-authentication expects a layout.haml for its pre-rolled authentication views.
How do I specify a different layout template that sinatra-authentication can use (e.g. auth_layout.haml) so that I can keep layout.haml for my app's views?
My current not-ideal approach to this is to:
Allow sinatra-authentication to use the standard layout.haml
Explicitly use another layout file (e.g. std_layout.haml) in all other parts of the app
e.g.
...
erb :home_page, :layout => :std_layout
...
I'd prefer it the other way around :)
You may read on Layout Engines or simply try:
get '/login' do
haml :login, layout: auth_layout
end
EDIT-1: your comment is Ok layout: auth_layout is ruby 1.9 syntax :layout => auth_layout is syntax used before 1.9
And if you are talking about this sinatra-authentication you must hack this file's app.get '/login/?' method regarding (Module::Helpers#use_layout?).
EDIT-2: I guess overwriting use_layout? method will help you, perhaps something like
Module Sinatra
Module Helpers
def use_layout?
request.xhr? ? false : :auth_layout
end
end
end
I'm writing a Sinatra app which needs to render different layouts based on whether the user is using an iPhone or a regular browser. I can detect the browser type using Rack-Mobile-Detect but I'm not sure of the best way to tell Sinatra which layout to use.
Also, I have a feeling that how I choose to do this may also break page caching. Is that true?
Example code:
require 'sinatra/base'
require 'haml'
require 'rack/mobile-detect'
class Orca < Sinatra::Base
use Rack::MobileDetect
helpers do
def choose_layout
if request.env['X_MOBILE_DEVICE'] == :iPhone
# use iPhone layout
else
# use normal layout
end
end
end
before do
# should I use a before filter?
choose_layout()
end
get '/' do
haml :home # with proper layout
end
end #Class Orca
This is what I ended up doing:
require 'sinatra/base'
require 'haml'
require 'rack/mobile-detect'
class Orca < Sinatra::Base
use Rack::MobileDetect
# HAML template options
# Use HTML5 doctype
set :haml, {:format => :html5 }
helpers do
def get_layout
# For AJAX (XMLHttpRequest) requests, don't use a layout
if request.xhr? then
#layout = false
exit
end
# For non-AJAX (XMLHttpRequest) requests, choose correct layout
# For each mobile device, you will need a layout_<device>.haml file
# in the Views directory
#layout = case request.env['X_MOBILE_DEVICE']
when /iPhone|iPod/ then :layout_iphone
# when "Android" then :layout_android
else true # use default Sinatra layout
end
end
end # helpers
before do
get_layout()
end # before filter
get '/' do
# Will use iPhone layout for iPhone|iPod,
# Sinatra default layout for desktop browsers
haml :home, :layout => #layout
end
end # Class
I believe the standard way to handle specific user agents in Sinatra is directly on the route...
get '/', :agent => /iPhone/ do
# render for iPhone
end
get '/' do
# render standard layout
end
See The Sinatra Book.
Re: caching, I guess it would depend on what caching layers are fronting your site, but, yes, you may need to account for this.
I wrote a blog post about this topic that might be helpful to someone using Padrino with Sinatra. If you're not using Padrino, this still might be useful if you find the right place to extend Sinatra.
http://blog.joshdzielak.com/override-padrino-locale-based-template-resolu
http://dzello.com/blog/2011/06/22/override-padrino-locale-based-template-resolu/
The summary - I use rack-mobile-detect to tell me if a request is 'mobile', and I patch out Padrino's locale-based rendering support to render based on the mobile detection instead of the locale.
In this way I'm able to have foo.mobile.haml render for mobile and foo.haml for non-mobile, without any application code. As a bonus, it works both for the template file and the layout.
I had a similar problem always we cant relay on Routes . Sinatra provides filters to handle these kind of problems
#your_layout_name = ''
before :agent => /iPhone/ do
#your_layout_name = "initialize with desired iphone template"
end
get '/' do
# use #your_layout_name variable to initialize layout
end
I posting this since I had a problem where I was not in a position to filter at route level
so This may help others who are looking for similar solutions