I am perplexed by something that isn't actually a practical problem - just a conceptual conundrum - about deploying a Sinatra app on Heroku.
I have two apps, identical in just about every respect, except that one puts most of its logic in a file that does not contain the Sinatra::Base class and uses a 'require otherfilename' to pick up the logic it needs. That Sinatra:Base class is named Kincalc.
For the app with all the logic in one file (that is, the same file that contains the Sinatra:Base class), in my config.ru file, the last line reads "run Sinatra::Application" and it launches fine. But in the other app, if I include that as the last line, the app uploads properly and says it was "deployed to Heroku" but it brings up a "Not found" message. When I have the last line read 'run Kincalc', it loads fine.
I have checked this back and forth and there is nothing different about how these two apps are built, except for the fact that one uses a second file for the logic (which is also at root). Why should this be a problem, or is this really the problem? When I try to put the second file (the one without the Sinatra class) in a separate lib folder, it still works when I call the class name but not when I call "Sinatra::Application."
Code at top level will be delegated to Sinatra::Application, so this would be a scenario for running a classic application:
# app.rb
require 'sinatra'
get '/' do
'hi'
end
# config.ru
require './app'
run Sinatra::Application
If you define a modular app, you would run it like this:
# app.rb
require 'sinatra/base'
class Kincalc < Sinatra::Base
get '/' do
'hi'
end
end
# config.ru
require './app'
run Kincalc
Now I assume what you are trying to do is this:
# otherfilename.rb
require 'sinatra'
get '/' do
'hi'
end
# app.rb
require 'sinatra/base'
class Kincalc < Sinatra::Base
require './otherfilename'
end
# config.ru
require './app'
run Kincalc # Sinatra::Application seems to work
The behavior you experience (getting a 404 File Not Found) is actually correct, as require does not care about the lexical scope it is called in. Check out the following example
# a.rb
puts "in a, top level: #{self.inspect}"
module Example
puts "in a, nested: #{self.inspect}"
require 'b'
end
# b.rb
puts "in b: #{self.inspect}"
The resulting output should be:
in a, top level: main
in a, nested: Example
in b: main
So, if you want to use one modular application, you should do something like this:
# otherfilename.rb
Kincalc.get '/' do
'hi'
end
Or open the class again:
# otherfilename.rb
class Kincalc
get '/' do
'hi'
end
end
Or you could actually have otherfilename.rb make it's definitions on Sinatra::Application and utilize that in Kincalc.
# app.rb
require 'sinatra/base'
require './otherfilename'
class Kincalc < Sinatra::Base
use Sinatra::Application
end
Or you could change where top level DSL methods are delegated to:
# app.rb
require 'sinatra/base'
class Kincalc < Sinatra::Base
Sinatra::Delegator.target = self
require './otherfilename'
end
Related
I recently began programming and learning Ruby and JavaScript and was attempting to read my html file through my Sinatra server using a config.ru file.
The server runs, its hitting all the routes but I think there may be something wrong with the server code for the index page:
get("/") do
content_type :html
File.read( File.expand_path("../views/index.html", __FILE__) )
end
Put index.html in public folder. Sinatra will serve files in public as is. So you need to request it directly e.g. http://localhost/index.html.
If you want to handle empty route i.e. get '/' use snippet below (from here):
get '/' do
send_file File.join(settings.public_folder, 'index.html')
end
In order to be sure in settings.public_folder please check does it work correctly, does it return correct path.
Cheers!
I am trying to use jruby + page-object gem + Cucumber for a proof of concept. I used the following statement.
app_url = 'https:\\google.com'
page_url(app_url)
I get a
NoMethodError: undefined method `page_url' for #
However,
navigate_to(app_url)
works fine. page_url works fine in Ruby.
Is this the way this works in jRuby? Though navigate_to works, is this any different?
Thank you for your help!
page_url is a class method provided by including the PageObject module. It sets the url for the page so you can use the visit_page factory in your test:
object MyPage
include PageObject
page_url "http://example.com/"
end
In a test somewhere:
visit_page MyPage do |page|
page.some_object_element.do_something
end
navigate_to is browser functionality exposed directly in your test via some World magic.
JSON really is a pain to use for local configuration files as it does not support comments or functions, and requires incredibly verbose syntax (commas, always use " for keys). Making it very error prone, or in the case where functions are required, impossible to use.
Now I know that I could just do:
require('coffee-script')
config = require('config.coffee')
However, that requires me to do module.exports = {the data} inside config.coffee which is less than ideal. And even allows for things such as require to be exposed which can make the configuration files insecure if we do not trust them.
Has anyone found a way to read coffeescript configuration files, but keep them secure?
Turns out CoffeeScript has support for the security part built in via setting the sandbox argument to true via the eval call. E.g.
# Prepare
fsUtil = require('fs')
coffee = require('coffee-script')
# Read
dataStr = fsUtil.readFileSync('path').toString()
data = coffee.eval(dataStr, {sandbox:true})
The above code will read in the file data, then eval it with coffeescript in sandbox mode.
I've created a nice wrapper for this called CSON which supports coffee and js files via require, and cson files via the above mechanism, and json files via the typical JSON.parse - as well as stringifying the values back to coffeescript notation. Using this, the following API is exposed:
# Include CSON
CSON = require('cson')
# Parse a file path
CSON.parseFile 'data.cson', (err,obj) -> # async
result = CSON.parseFile('data.cson') # sync
# Parse a string
CSON.parse src, (err,obj) -> # async
result = CSON.parseSync(src) # sync
# Stringify an object to CSON
CSON.stringify data, (err,str) -> # async
result = CSON.stringifySync(obj) # sync
I'm creating a web-app.
While it works great, writing whole pieces of code inside "<% %>" tags in ruby on rails is pretty ugly.
I tried to define a function in my controller.rb and then call it from the html page.
This does not work, as it does not recognize the function.
I'd appreciate any help here. Did I put my function in the correct place? Do I need to load using "require"?
For example:
controller file:
class WelcomeController < ApplicationController
def index
end
def myfunc(x)
puts x
end
end
HTML file (index.html):
<h1>Welcome</h1>
<p>
<%= myfunc(5) %>
</p>
What you are referring to is called a helper in Rails. There are two ways that you could implement this.
Option number one is to place the method you want to access inside a helper module. The most common one is ApplicationHelper which you can find in RAILS_ROOT/app/helpers/application_helper.rb. If you place the method in there it will be accessible from the views.
Another way if you still want/need to have the method in the controller, then you can use the helper_method function like this:
def WelcomeController < ApplicationController
helper_method :my_func
private
def my_func(x)
puts x
end
end
The usage of private is not needed, but only good practice so that the method cannot be accidentally used as a Controller action or something.
Ruby on rails has a Model-View-Controller architecture - in those architectures you can't access the controller from the view.
You could set a variable with the output from your function, and access that variable from your view
I have a simple grails file upload app.
I am using transferTo to save the file to the file system.
To get the base path in my controller I am using
def basePath = System.properties['base.dir'] // HERE IS HOW I GET IT
println "Getting new file"
println "copying file to "+basePath+"/files"
def f = request.getFile('file')
def okcontents = ['application/zip','application/x-zip-compressed']
if (! okcontents.contains(f.getContentType())) {
flash.message = "File must be of a valid zip archive"
render(view:'create', model:[zone:create])
return;
}
if(!f.empty) {
f.transferTo( new File(basePath+"/files/"+zoneInstance.title+".zip") )
}
else
{
flash.message = 'file cannot be empty'
redirect(action:'upload')
}
println "Done getting new file"
For some reason this is always null when deployed to my WAS 6.1 server.
Why does it work when running dev but not in prod on the WAS server? Should I be accessing this information in a different way?
Thanks j,
I found the best dynamic solution possible. As a rule I never like to code absolute paths into any piece of software. Property file or no.
So here is how it is done:
def basePath = grailsAttributes.getApplicationContext().getResource("/files/").getFile().toString()
grailsAttributes is available in any controller.
getResource(some relative dir) will look for anything inside of the web-app folder.
So for example in my dev system it will toString out to "C:\WORKSPACEFOLDER\PROJECTFOLDER\web-app\ with the relative dir concated to the end
like so in my example above
C:\WORKSPACEFOLDER\PROJECTFOLDER\web-app\files
I tried it in WAS 6.1 and it worked in the container no problems.
You have to toString it or it will try to return the object.
mugafuga
There's a definitive way...
grailsApplication.parentContext.getResource("dir/or/file").file.toString()
Out of controllers (ex. bootstrap)? Just inject..
def grailsApplication
Best regards!
Grails, when it's run in dev mode, provides a whole host of environment properties to its Gant scripts and the app in turn, including basedir.
Take a look at the grails.bat or grails.sh script and you will find these lines:
Unix: -Dbase.dir="." \
Windows: set JAVA_OPTS=%JAVA_OPTS% -Dbase.dir="."
When these scripts start your environment in dev mode you get these thrown in for free.
When you take the WAR and deploy you no longer use these scripts and therefore you need to solve the problem another way; you can either
Specify the property yourself to the startup script for the app server, eg: -Dbase.dir=./some/dir .. however
... it usually makes more sense to use the Grails Config object which allows for per-environment properties
Another option:
def basePath = BuildSettingsHolder.settings.baseDir