Mustache & Sinatra - is it possible to omit the view.rb file? - ruby

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

Related

Is it possible to use methods defined in refinements in Sinatra views?

I have a class defined in a gem to which I am adding some methods via refinements (in Ruby 2.3.0). This class turns up in some Sinatra views (haml).
When I refer to these extra methods in a helper, there is not a problem. But in the view, I get an Undefined Method error.
Am I missing a trick, or is it that the using ... statement would need to go somewhere I just can't get to?
(Workaround: I can define helper methods to return the method on the object. But if I wanted to do that then I wouldn't have used refinements...)
The scope of refinement is determined lexically. Unless you rewrite the method inside haml that calls that method so that it becomes within the scope of the using command, you cannot use refinements. But I guess haml is internally using eval or something like that to evaluate the code you write in a haml file. In that case, it is impossible.

How to test a Padrino controller from Padrino console

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

Where and how should I add this new 'non-rails-way' method in my Rails application

I've written a small method to query and retrieve from an MS SQL 2008 server and I am not sure where to put the code in my rails app.
The scenario:
I am writing a Ruby and Rails app with a connection to a legacy MS SQL 2008 server DB.
A lot is working as expected, which is nice.
For now I work off a copy of the legacy DB and I treat it as readonly. It's big (7000+ tables some of which have over 40 million records). I am using it 'as-is' and don't want to change any of the underlying schema.
I do want to extend some very server-specific functionality. For instance, I make use of:
thing = ActiveRecord::Base.connection.exec_query(my_query_string_here)
... and it works. The result is an array that contains a hash and I can get to the relevant hash value by using:
thing[0][""]
... which works.
So, I thought I should write a method to make this easier and I wrote:
Class Tool < ActiveRecord::Base
def self.queryRDW(x)
res=ActiveRecord::Base.connection.exec_query(x)
ret=res.to_hash
return ret[0][""]
end
end
and put it in config/initializers/tool.rb Unfortunately, webrick complains about the file during boot with the following cryptic error:
.../config/initializers/tool.rb:7: syntax error, unexpected keyword_end, expecting $end (SyntaxError)
I recognize that this is not an out-of-the-box rails-way of doing things, so please don't remind me. (My struggles remind me often enough)
My question:
Where should I put this code so that I can invoke it from within a controller or a view in my rails app? Does this need to be a new Class method or something else?
Many thanks!
Addendum:
I changed Class to class (doh!)
I moved tool.rb into lib/
I changed tool.rb to now be:
module Tool
def self.queryRDW(x)
res = ActiveRecord::Base.connection.exec_query(x)
res.to_hash[0][""]
end
end
but doing this in app/views/stats.html.erb
thing=queryRDW("mysql string")
gets me an 'undefined method error'
Addendum 2
I made the directory app/concerns and put tool.rb there.
When I use:
<%=queryRDW("myStringHere")%>
in:
app/views/stats.html.erb
I get:
undefined method `queryRDW' for #<#<Class:0x0000000378ccf8>:0x00000003c1ce58>
You need to lowercase the keyword class in line 1.
I'd also say that this class doesn't need to inherit from ActiveRecord::Base — and doesn't even really need to be a class — if it's simply a wrapper around exec_query. There's nothing "wrong" with this, but if you never intend to instantiate an object of this class, you could just create a simple utility module:
module Tool
def self.queryRDW(x)
res = ActiveRecord::Base.connection.exec_query(x)
res.to_hash[0][""]
end
end
You can save this file in a couple of places:
lib/tool.rb. If you're using Rails 3, you'll need to add (or uncomment) this line in config/application.rb:
# config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
app/concerns/tool.rb. This will automatically be detected by Rails 3.
I generally use app/concerns for tools that are entirely application-specific and lib for utilities that I might reuse among several different applications.
I wouldn't put this in config/initializers. This seems like code you'd put in app/models.
The error you're getting is syntax related, so double check the syntax.
To answer your question more directly, though, it's acceptable to put this stuff in your model if it's model related (in other words, part of your business domain). If it is something extraneous or orthogonal to your domain, I'd put it in lib.
Hope this helps.

If I want to add a method to String class in Sinatra, where should I put it?

I want to expand a method to the String class in Sinatra, in the erb file, do something like
<%= 'some string'.my_method %>
but I don't know how to put the definition code:
String.class_eval do
def my_mythod
some_code
end
end
By the way I'm using the sinatra modular coding style
I tend to stick code like this in its own file, under the lib/ext folder. Then, you can require this file from your Sinatra app.
Under lib/ext/string.rb:
class String
my_mythod
some_code
end
end
Then add the following to your Sinatra app, assuming your base class file is inside the lib folder:
require File.dirname(__FILE__) + '/ext/string'
I'd be interested to see what over people think on this as well.
seems to work wherever i put it (is this a good observation?)
anywhere that is evaluated on script start
drop it in the app class itself along with all the other fields and methods
should work in the helper class too

what is a controller in sinatra?

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.

Resources