Erb template not rendering when used with ensure exception handling - ruby

I have come across a issue when writing some sinatra code I have the follow block of code
begin
# do stuff here
rescue SomeException::Class => ex
flash.now[:err] = "some error " + ex.message
ensure
erb :content, :layout => :mainlayout
end
The problem I have is the erb output is only partially rendered, mainlayout.erb is rendered, however, content.erb doesn't get included. I have used the same erb line in other parts of the application and they work fine.
The following actually works and is a work around that I am currently using
begin
# do stuff here
erb :content, :layout => :mainlayout
rescue SomeException::Class => ex
flash.now[:err] = "some error " + ex.message
erb :content, :layout => :mainlayout
end
Any ideas to why this isn't completing when under ensure? I would like to use it as its more elegant.

try to use return erb :content, :layout => :mainlayout read more about ensure here - http://blog.leshill.org/blog/2009/11/17/ensure-with-explicit-return.html

Related

Ruby: How to pass a variable from server to index?

something = "0"
get "/" do
erb :index, :locals => something
end
When I do this and go to localhost, it says undefined method `keys' for "0":String. I am using sinatra. How do I pass a variable from server to index?
You're half way there, you just need to make something an object.
get "/" do
erb :index, :locals => {:something => 0}
end
and then just use it in the your index:
Something: <%= something %>

Syntax error in content type case statement

This is my code in my Padrino application and I can't figure out what line or bug it is. The error message is "syntax error, unexpected keyword_end expecting $end"
get :index, :provides => [:html, :json] do
#title = "Restaurants"
#restaurants = Restaurant.all
case content_type
when :json
render #restaurants
else
render 'restaurants/index'
end
end
end
Could you please point out my mistake and also suggest how I might debug it in future? Thanks
You have one spare end keyword.
You should remove one.
There is a little mess with indentation in your code. Keeping right indentation helps a lot in avoiding such errors. I would suggest to indent your code like this:
get :index, :provides => [:html, :json] do
#title = "Restaurants"
#restaurants = Restaurant.all
case content_type
when :json
render #restaurants
else
render 'restaurants/index'
end
end
There's an end too much.
Be more careful with your code indentation and this will never be a problem. Example on how it would look in Vim under. I just used =G and it aligned it for me. Additionaly, it'll only highlight the correct use of end. Your favorite editor of choice should have this functionality too. If not, switch.
try this:
get :index, :provides => [:html, :json] do
#title = "Restaurants"
#restaurants = Restaurant.all
case content_type
when :json
render #restaurants
else
render 'restaurants/index'
end
end

Ruby Sinatra + Sequel constraint error handling

What is the right way to handle exceptions coming from the model in Sequel? Particularly the thing I'm running into is when the unique constraint is applied to the login. The exception in this case appears to be coming from SQLite itself instead of Sequel which means it's not getting handled by "errors".
This is the error I'm getting after trying to create a user with a "non-unique" login:
Sequel::DatabaseError at /user/create
SQLite3::ConstraintException: column login is not unique
file: database.rb location: close line: 97
Here is my abbreviated code:
require 'sinatra'
require 'sequel'
DB.create_table :users do
primary_key :id
String :login, :key => true, :length => (3..40), :required => true, :unique => true
String :hashed_password, :required => true
String :salt
DateTime :created_at, :default => DateTime.now
end
class User < Sequel::Model
# password authentication code
end
get '/user/create' do
slim :user_create
end
post '/user/create' do
user = User.new
user.login = params['login']
user.password = params['password']
if user.save
'User created'
else
tmp = []
user.errors.each do |e|
tmp << (e.join('<br/>'))
end
tmp
end
end
You probably want to use the validation_helpers plugin and use validates_unique :login inside the validate method.
Add the code below to handle sequel errors in sinatra.
The error block in sinatra will handle any errors thrown in one of your routes. You can specify the type of error in the first line in order to only handle those types of errors.
error Sequel::Error do
e = env['sinatra.error']
content_type :json
status(400)
return {
message: e.message
}.to_json
end
If you wanted to handle all errors, you would insert the following block of code.
error Exception do
e = env['sinatra.error']
content_type :json
status(400)
return {
message: e.message
}.to_json
end
You can combine many of these blocks so that you are handling different types of errors differently.
First - use validation plugin
# models/account.rb
Sequel::Model.plugin :validation_helpers
class Account < Sequel::Model
def validate
super
validates_unique :login
end
end
Next - handle error in app
# app.rb
...
begin
Account.create
rescue => e
# do what you need
end
...

Template functions with HAML in Sinatra

I'd like to be able to create template functions in Sinatra HAML templates that themselves contain haml. Is there any way to do this or something similar? It'd be cool if it could work with markdown too.
foo.haml
def foo(x)
%h2 something
%p something about #{x}
%h1 Herp de derp
= foo("mary")
= foo("us")
Cheers!
Actually, you can do something like this:
# app.rb
require 'sinatra'
require 'haml'
helpers do
def foo(name)
haml = <<-HAML
#hello_block
Hello, #{name}
HAML
engine = Haml::Engine.new(haml)
engine.render
end
end
get '/' do
haml :index
end
# index.haml
= foo 'World'
Function is close, what you really need is what's known as a partial. These are predefined templates that you can place inside other views. For instance, you may have a comment partial to display a comment's author, timestamp, content, etc. You can then render this partial for each of the comments on a particular post.
Essentially, you'll end up with the following
# _foo.haml.erb
%h2 somthing
%p= x
# index.haml.erb
%h1 Herp de derp
= render :partial => "foo", :locals => { :x => "mary" }
= render :partial => "foo", :locals => { :x => "us" }

Sinatra render a ruby file

How to render a ruby file (which in my case returns a pdf document) for example:
get "/pdf" do
#locals = {some_locals_hash}
headers({'Content-Type' => 'application/pdf',
'Content-Description' => 'File Transfer',
'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' => "attachment;filename=\"test.pdf\"",
'Expires' => '0',
'Pragma' => 'public'})
ruby :test, :layout => false, :locals => #locals
end
I know Tilt does not have ruby template. For now I put all content in a *.haml file like:
-# PDF file description
:ruby
pdf = Prawn::Document.new(
... docs settings)
... docs content
end
= pdf.render()
and I render it with haml :template ...etc...
Truth is, I only need this for syntax highlighting, my editor does not properly highlight embedded ruby code in haml files :(. So if it's to complicated don't bother...
I managed with a Tilt template
module Tilt
class RubyTemplate < Template
def prepare
end
def evaluate(scope, locals, &block)
super(scope, locals, &block)
end
def precompiled_template(locals)
data.to_str
end
end
register 'rb', RubyTemplate
end
and with a helper method
helpers do
def ruby(*args)
render(:rb, *args)
end
end
I't sure this is the best way, but at least is working :)

Resources