I was asked why "I was creating complex Ruby variables in my view.
Shouldn't have these variables been declared by my controller?"
Is my sinatra controller my .rb file? I have one .rb file and view views.
You can setup the idea of controllers by doing (in 1.9.2) this at the top of your main .rb file
Dir.glob("controllers/*.rb").each { |r| require_relative r }
This will require_relative each .rb file in a folder called controllers/
From there you can implement normal routing like you would've previously done in the main .rb file. Please have a look at rstat.us on Github.
Edit: Rstat.us has gone rails3 and while still helpful you may have to go back numerous commits on the master branch to find how it was used.
Each Sinatra route can be considered its own controller in a typical MVC setup. For your example:
require 'sinatra'
require 'json'
get "/foo" do
# This might take many lines of excellent code to form your data
#data = some_complex_array_hash_combo
haml :foo
end
And then in foo.haml:
:javascript
var data = #{#data.to_json};
Sinatra out of the box does not have a standard MVC framework. So while you don't want to leave everything in the main view file, you also don't technically have a "controller" to put this in. Splitting up your application into different functionality would probably be the best approach to keep it simple. Pull large areas of functionality out into separate classes and small things into helper libraries.
Looking at how others do this might help out, this post should have some good examples for you to study: https://stackoverflow.com/questions/2075758/real-life-examples-of-sinatra-applications
If an MVC framework becomes something you really think you need, take a look at Padrino (http://padrinorb.com)
Slightly related post:
https://softwareengineering.stackexchange.com/questions/14293/ruby-sinatra-best-practices-for-project-structure
#CaleyWoods : thank you for the reference to rstat.us
For those who are looking for the Sinatra version, here is a link to a Sinatra commit:
https://github.com/hotsh/rstat.us/tree/00b27505681d80b3943fd9b9e9791f664a04cf39
(so you don't have to trawl through the commit history ;-) )
This is just for inheritance later if you have controllers that inherit from ApplicationController. Good luck!
If your using a config.ru file for your app then this may help.
require 'active_support'
require 'sinatra/base'
APP_ROOT = Pathname.new(File.expand_path('../', __FILE__))
# We have to do this in case we have controllers that inherit from each other.
Dir[APP_ROOT.join('app', 'controllers', '*.rb')].each do |controller_file|
filename = File.basename(controller_file).gsub('.rb', '')
autoload ActiveSupport::Inflector.camelize(filename), controller_file
end
This assumes you put that code into your config.ru but you could put in your application file also and be sure to adjust for directory structure.
Related
All helpers in Sinatra can be accessed from both my HAML code and my routes. But I'm finding myself writing two completely separate sets of code for these two.
Ideally I'd like to keep them apart. Currently I have them in two different directories, but that seems kind of pointless. Can anyone suggest a way I can limit the potential damage caused by namespace collision?
At first I thought modules. From the Sinatra readme:
module FooUtils
def foo(name) "#{name}foo" end
end
module BarUtils
def bar(name) "#{name}bar" end
end
helpers FooUtils, BarUtils
But is there any point in actually doing this? Aren't they going to have to share the same namespace inside my Sinatra app?
Some tentative and not-entirely-satisfactory answers:
1) Methods defined in the same way as helpers but without the call to Sinatra::helpers don't appear to be accessible from views, but are accessible from within the Sinatra application block. That is:
require 'sinatra'
module Sinatra
module MyThing
# Helper classes and methods go here
end
helpers MyThing # <- leave this out if you don't want access from views
end
2) You can of course also have MyThingHelpers and MyThingLib modules, instead of just MyThing, to prevent namespace collision.
3) And you can put them in separate subdirectories.
I've yet to be convinced that these represent a comprehensive solution to my concerns. Time and testing will tell, I suppose, unless someone can give me a better answer here.
I know in Rails I can do something like
app.get 'url'
app.response
But this does not work in Padrino, nor do any of the regular controller calls because Padrino uses different controller methods than Rails.
What I'm trying to do is test my controller methods from the Ruby Padrino MRI console. For example, I want to store the objects present, call the same method 100 times, then compare what objects are left behind. I'm trying to find a memory leak.
So it would be great to be able to call the method from a Padrino console.
I can't find anything that tells me how to do it in the official documentation or elsewhere.
The get in you Padrino::Application is just part of the DSL to define new routes, not to retrieve their contents. What you are trying to achieve is usually part of the Rack::Test library.
Have a look at the Sinatra documentation:
http://www.sinatrarb.com/testing.html
Specially compare the sections about Rack::Test with Mixin VS without Mixin. That should make you understand where the fetching get comes from.
In your case, if you want to test from the console, then it should be something like this part:
require 'rack/test'
browser = Rack::Test::Session.new(Rack::MockSession.new(Padrino::Application))
browser.get '/'
Now, where you see Padrino::Application you must type your own application main class that inherits from that class, not the abstract class itself
Notice that the result will be a Rack::MockSession object, so if you just want to see the html do:
browser.get('/').body
I am trying to write a Sinatra application that groups components together (sort of like controllers). So for the "blog" related things, I want an app called Blog mounted at /blog. All of the routes contained in the Blog app would be relative to its mounted path, so I could simply define an index route without having to specify the mount path in the route.
I originally handled this by using a config.ru file and maping the routes to the different apps. The problem with this that I ran into, was that I was using various sinatra gems/extensions/helpers that needed to be included in all of the apps, so there was a lot of duplicate code.
How can I mount one sinatra app inside of another so that the routes defined in the app are relative to where the app is mounted? If this is not possible out-of-the-box, can you show a code sample of how this could be done?
Here's a simplified example of what it might look like:
class App
mount Blog, at: '/blog'
mount Foo, at: '/bar'
end
class Blog
get '/' do
# index action
end
end
class Foo
get '/' do
# index action
end
end
Take a look at https://stackoverflow.com/a/15699791/335847 which has some ideas about namespacing.
Personally, I would use the config.ru with mapped routes. If you're really in that space between "should this be a separate app or is it just helpful to organize it like this" it allows that, and then later you can still farm off one of the apps on its own without changing the code (or only a little). If you're finding that there's a lot of duplicated set up code, I'd do something like this:
# base_controller.rb
require 'sinatra/base'
require "haml"
# now come some shameless plugs for extensions I maintain :)
require "sinatra/partial"
require "sinatra/exstatic_assets"
module MyAmazingApp
class BaseController < Sinatra::Base
register Sinatra::Partial
register Sinatra::Exstatic
end
class Blog < BaseController
# this gets all the stuff already registered.
end
class Foo < BaseController
# this does too!
end
end
# config.ru
# this is just me being lazy
# it'd add in the /base_controller route too, so you
# may want to change it slightly :)
MyAmazingApp.constants.each do |const|
map "/#{const.name.downcase}" do
run const
end
end
Here's a quote from Sinatra Up and Running:
Not only settings, but every aspect of a Sinatra class will be inherited by its subclasses. This includes defined routes, all the error handlers, extensions, middleware, and so on.
It has some good examples of using this technique (and others). Since I'm in shameless plug mode I recommend it, even though I've nothing to do with it! :)
I just meet the same problems.
I found that whatever inclued extend register or rack middleware style use had problems.
You can use Ruby to solve this problems like that:
require "sinatra"
class App < Sinatra::Base
class << self
def define_routes(&block)
class_eval(&block)
end
end
end
App.define_routes do
get "/hello"
"hello world"
end
end
I would like to allow anonymous access to my forums. I am using the Forem Gem and the Devise Gem. I have a before_filter in my ApplicationController that ensures users are logged in.
before_filter :authenticate_user!
In my own controllers I use skip_filter to allow anonymous access to actions but I do not know how to do this for the controllers in the Forem Gem.
I could move the before_filter statement from my ApplicationController into each of my own Controllers but I would rather not.
Thanks for the answer Ryan, Just like to point out that the file should be:
forem/application_controller_decorator.rb otherwise it won't load with that initialiser code (note: underscore - not dash).
And to re-iterate, the initialiser code should be :
Rails.application.config.to_prepare do
Dir.glob(Rails.root.join("app/**/*_decorator*.rb")) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
end
So that it will reload on every request in development mode.
Hopefully that will save someone a few hours!
The best place for this would be a file inside your app/controllers directory at forem/application_controller-decorator.rb that contains this content:
Forem::ApplicationController.class_eval do
skip_before_filter :authenticate_member!
end
You would then need to include this file using lines like this in a file called config/initiailizers/load_decorators.rb:
Dir.glob(Rails.root.join("app/**/*_decorator*.rb")) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
This would allow you to specify other decorators as well, for any other class of Forem, if you so desired.
I stole this idea from Spree (which I work on full-time now), and I think it's pretty solid. You can see the original implementation of it here.
I'm using the Mustache templating library with Sinatra and the standard way seems to be to create, say, index.mustache under /templates and an associated index.rb that subclasses Mustache in /views.
For things like the About page, where no special logic happens at all, how is it possible to use only a .mustache template and still do the following in Sinatra:
get "/" do
mustache :about
end
When I simply don't provide the index.rb file, Mustache throws an error about not being able to find it.
I think the solution is very simple. If you don't have a view model (like index.rb) you just use another template system like erb
erb :about
or if the file is static just put it here
./public/about.html
I doesn't make sense to hava .mustache template without a view model.
That view requirement is annoying. Jason Campbell comes to the rescue with https://github.com/jxson/sinatra-mustache