I would like to mount a sinatra application in my rails app.
But I would like this one to share the same layout.
The iframe could work but do you have any other idea ?
Thanks
You basically need to do two things:
You need to tell the Rails router that a certain URL path is to be handled by another Rack app (in your case a Sinata app). This can be done by adding this to your routes.rb:
match "/sinatra" => MySinatraApp, :anchor => false
Having done that, you can create your app like so:
class MySinatraApp < Sinatra::Base
get "/" do
"Hello Sinatra World"
end
end
The second step now is to tell your Sinatra app to use the rails layout which by default lives in app/views/layouts/application.html.erb for Rails 3.1. by default, Sinatra uses ./views/layout.ext (with ext being the extension of your chosen template system). So you basically, have to tell Sinatra to
use another directory to find views and layouts instead of the default ./views
use another template file as the default layout.
Both can be achieved by setting the following in your sinatra app:
set :views, "/path/to/your/railsapp/views"
set :erb, layout => :"layout/application" # or whatever rendering engine you chose
to share the same layout, you can point sinatra to the folder where the layout is in your rails app:
(taken from here: http://www.sinatrarb.com/configuration.html)
:views - view template directory A string specifying the directory
where view templates are located. By default, this is assumed to be a
directory named “views” within the application’s root directory (see
the :root setting). The best way to specify an alternative directory
name within the root of the application is to use a deferred value
that references the :root setting:
set :views, Proc.new { File.join(root, "templates") }
From your Rails app you can build a method which you can call from the action where the sinatra app should be included in the view.
(given you want to use the index action for this)
def index
#sinatra_content = get_sinatra
end
# use #sinatra_content in your views for rendering
def get_sinatra
sinatra_ip = 127.0.0.1;
sinatra_port = 4567;
#start a request here
RestClient.get 'http://#{sinatra_ip}:{sinatra_port}/', {:params => {:id => 50, 'foo' => 'bar'}}
end
see how rest-client works here: https://github.com/archiloque/rest-client and don't forget to include the gem in your rails app.
To use links in your sinatra app you should decide if sinatra should handle this (point to sinatra app (with port) or build links in your sinatra app which are handled by your rails app)
I think that using the append_view_path in your rails application will work a little bit better. Just append the Sinatra views to your Rails app and it will look there after looking in app/views.
The Crafting Rails Applications book by José Valim has a lot of documentation on that topic (rendering views from other sources), you may want to look at that.
Also, this Railscasts can help: http://railscasts.com/episodes/222-rack-in-rails-3
Related
I have a few Rack apps mounted within my routes.rb like so:
mount ImageVise, at: '/ivise'
mount ShaderApiV0, at: '/api/v0'
I am currently migrating from a spaghetti custom Rack stack to Rails, so it makes sense for me to keep those apps. I am noticing that the entire Rails middleware stack that is defined on the application (including my Rack::Cache setup) does function when I call a controller, but does not function when I call URLs controlled by those mini-apps. I am almost certain in Rails 4 it worked however.
For example, if I introduce a custom middleware like so:
class Mittel < Struct.new(:app)
def call(env)
a, b, c = app.call(env)
b['X-Kustom'] = 'olala'
[a,b,c]
end
end
config.middleware.insert_before Rack::Head, Mittel
I do see the X-Kustom response header when I request a URL that is driven by the Rails controllers, but I do not get it when I request one of the URLs controlled by the mounted mini-apps. Consequently conditional GET and things like that do not work etc. How can I make it work aside from replicating a third of the Rails middleware stack in config.rb and moving these apps mounts there?
Found it. Turns out I was mounting one of these apps in config.ru as well, under the same URL - and then of course the entire Rails stack gets bypassed, as it should be. Lesson learned.
Some page in the application must be accessible even application is down. For example pages for 50x errors. The easiest way to do so - create static HTML pages, which will be served by web-server (like apache on Nginx). Most of this pages have a common layout with the application. So, if we change some part of layout in the application we must change all static pages by hand.
What is the best way to store rails pages as static files and recreate it (automatically or by rake task) on same changes in the project? Is any gem for rails or static-site generator that's able to reuse rails layout and resources (CSS, js, images).
Generally static content goes in your public folder which you can configure Nginx or equivalent to route to accordingly without even needing to hit Rails.
For static site generation in Ruby you might want to check out Jekyll https://jekyllrb.com/. You could manage your Jekyll site separately from your Rails site and generate the static HTML/CSS/JS on deployment. There's a jekyll watch command that will listen for file edits and compile your static content accordingly.
There simple rake task (via GIST) to load all files from VIEW_PATH, wrap it with application layout and store it under same path in public. Work with Rails 4.
https://gist.github.com/potapuff/090b2da4a4156c1272430241cb70edc0
namespace :static do
desc 'Render all resources'
task :publicate => :environment do
resources(VIEW_PATH).each do |src, dest|
html= controller.render_to_string(file:src, layout:'application')
dirname = File.dirname(dest)
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
File.write(dest, html)
end
end
def resources search_path
...
end
def controller
ApplicationController.new.tap do |controller|
...
end
end
end
Other possibility is using gem render_anywhere .
In Rails 5 we have new ability to use render outside controllers
https://medium.com/evil-martians/the-rails-5-post-9c76dbac8fc#1b36
I'm trying to move my full-stack rails app over to Angular a page at a time. I'm using ui-router (https://github.com/angular-ui/ui-router) and angular-rails-templates (https://github.com/pitr/angular-rails-templates). I assumed the nghaml extension would allow me to continue to use rails helpers such as link_to, paths, etc. in my haml so just copied and pasted my haml page into the template; in an ideal world I would now be at a point where one page was being served client-side and every other page (including the ones it's linked to) were still being served server-side. Instead, I'm getting errors such as:
undefined local variable or method `dashboard_patients_path' for #<Object:0x007fc87865cff0>
and link_to, etc.
I thought this (angularjs with client side haml) would be a solid solution, specifically sharpper's response since it seemed directly applicable.
module CustomHamlEngine
class HamlTemplate < Tilt::HamlTemplate
def evaluate(scope, locals, &block)
scope.class_eval do
include Rails.application.routes.url_helpers
include Rails.application.routes.mounted_helpers
include ActionView::Helpers
end
super
end
end
end
Rails.application.assets.register_engine '.haml', CustomHamlEngine::HamlTemplate
However, even after restarting the server, no dice.
Thoughts?
Got stuck with the same problem and found a solution after investigating angular-rails-templates, attentively reading their documentation and using the solution proposed by sharpper in angularjs with client side haml.
angular-rails-templates needs to recreate a mimeless version of the haml engine. So they extend the classes that are registered with Tilt instead of using the engines that were added to the asset pipelines. Therefore the new CustomHamlEngine that we create and register with the asset pipeline is never used by angular-rails-template. What we need to do instead is to register the engine with Tilt.
Create a file called angular_rails_templates.rb in the config/initializers folder and put this code in it.
# config/initializers/angular_rails_templates.rb
module CustomHamlEngine
class HamlTemplate < Tilt::HamlTemplate
def evaluate(scope, locals, &block)
scope.class_eval do
include Rails.application.routes.url_helpers
include Rails.application.routes.mounted_helpers
include ActionView::Helpers
end
super
end
end
end
Tilt.register CustomHamlEngine::HamlTemplate, '.haml'
That will override the usual .haml engine with the one we just created. Angular-rails-templates will then process your haml file and it will support the rails helpers as well as the path helpers.
Don't forget to restart the server after including the file.
I'm trying to get custom scaffolding working from my engine.
I followed some tutorial on customizing Rails 3.2 scaffolding in a normal Rails App and put my customized templates in the engines /lib/templates/erb/scaffold directory but they don't get picked up by the app that includes the engine. Any suggestions?
Update:
I also tried to override the Rails ScaffoldGenerator's source_path and tried some other paths to put my template in, like:
lib/rails/generators/erb/scaffold/templates
zarazan's answer got me most of the way there, but there are a couple of things wrong with it. Here's what worked for me:
class Engine < Rails::Engine
config.generators do |g|
g.templates.unshift File::expand_path('../../templates', __FILE__)
end
end
Note that this goes in the generators section, not app_generators, and that the path is slightly different.
Also, I think the correct path to store your templates is lib/templates/erb/scaffold, optionally replacing erb with whatever language you are using (like haml or slim.) I know this works for slim. The file names are {_form,edit,index,new,show}.html.erb.
In the file that you declare your engine use this command:
class Engine < Rails::Engine
config.app_generators do |g|
g.templates.unshift File::expand_path('../templates', __FILE__)
end
end
It should shift the preference of what template folder Rails uses by default.
Now just put the template files in lib/templates/erb/scaffold/template_name.erb
Where template_name is one of the following: _form.html.erb, edit.html.erb, index.html.erb, new.html.erb, show.html.erb
Once you include the gem you should be able to use the rails generate scaffold command as normal.
Here is an example of an engine that overrides the default scaffolding in rails:
https://github.com/brocktoncg/gemboree
This is where the template directory is located:
https://github.com/brocktoncg/gemboree/tree/master/lib/templates/erb/scaffold
Are you talking about a controller template? Than you are using the wrong directory. Save your template at
lib/templates/rails/scaffold_controller/controller.rb
Have a look at http://xyzpub.com/en/ruby-on-rails/3.2/templates.html for an example.
Is it possible to get the value of named route from with in a custom rack app when the app is mounted in rails 3 (in my case a Sinatra app)?
Simply using the route, (login_path) is throwing an exception for an undefined local variable.
UPDATE:
Here is an example, of what I am trying to do:
before do
redirect login_path unless some_condition
end
The app is mounted with
mount App.new, :at => '/path'
This part works as expected.
Thanks,
Scott
Accessing the hosting rails app's routes in the mounted Sinatra might not be very elegant, since the hosted Sinatra should not have knowledge of the app that hosts it.
So instead, it'd better to do this in the rails app.
If you use devise, you can surround your mount block as this:
authenticate "user" do
mount App.new, :at => '/path'
end
This can be done because devise itself is a middleware added before route.
Devise implements this as:
def authenticate(scope)
constraint = lambda do |request|
request.env["warden"].authenticate!(:scope => scope)
end
constraints(constraint) do
yield
end
end
If you don't use devise, you might need to implement something similar.
I think this is not possible because they have separate code. You're just telling to rails what to do with certain paths, an it routes those requests to another rack app, they don't share anything about internal code.
maybe you can write some code to tell Sinatra how to read rails routes. a good place to start from:
http://apidock.com/rails/ActionDispatch