Really static pages with Rails - ruby-on-rails

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

Related

How to serve static files in Rails from a mounted path

I am trying to develop a gem to be used in Rails applications that includes serving static files (html, js, and css) at a mounted location. Using the gem's UI will be accomplished by adding the following to config/routes.rb
mount MyGem::UI.new, at: "/foo"
I have this almost working using Rack::Static, but am not attached to this solution if there is a better one.
module MyGem
module_function
def root
#root ||= Pathname(__dir__).expand_path
end
end
module MyGem
class UI
def call(env)
static_app.call(env)
end
def static_app
Rack::Static.new(nil, static_options)
end
def static_options
{urls: [""], root: asset_path, index: 'index.html'}
end
def asset_path
#root ||= MyGem.root.join("assets")
end
end
end
The files at MyGem.root/assets can be reduces to a single html file that links to a single css file in the same location using href="./index.css". (In reality they will be a React app built with homepage: "./" in package.json using create-react-app to produce ./ relative links to all assets.) I have tested this setup using both the dummy and the full-fledge React app.
This setup works if and only if I go to localhost:3000/foo/ with the trailing slash. However, if I navigate to localhost:3000/foo without the trailing slash, I successfully get the index.html page, but all of its links are broken, returning 404 not found:
GET http://localhost:3000/index.css net::ERR_ABORTED 404 (Not Found)
How can I avoid this and robustly server my static content at the mounted location, "/foo"? It's halfway acceptable as a pilot to just require the trailing slash, but is a bit dodgy to leave in a published gem.
I am open to solutions involving alternate rails routes configurations, different ways to serve the static content (contingent on being easily describable to gem users), and different ways to build the static content, and anything else.

How to tell Rails assets that new public asset file was generated

I have a Rails application that is dynamically creating and compiling assets depending from what domain you are accessing the website (let say different colors, where values of colors are stored in database )
let say for www.hello.example it will generate public/assets/hello-application-52777d36dec6f6e311560156da9da1c2.css so browser source file will point to
layout:
= stylesheet_link_tag 'hello-application'
generated html:
<link href="/assets/hello-application-52777d36dec6f6e311560156da9da1c2.css" media="all" rel="stylesheet" />
If you add www.goodbye.example it will compile public/assets/goodbye-application-52777d36dec6f6e311560156da9da1c2.css so browser source file will point to
layout:
= stylesheet_link_tag 'goodbye-application'
generated html:
<link data-turbolinks-track="true" href="/assets/goodbye-application-52777d36dec6f6e311560156da9da1c2.css" media="all" rel="stylesheet" />
Assets are compiled correctly on the fly without need to restart the server.
Examples above work fine if I restart the server
The thing I cannot figure out is how to tell Rails that new public asset file was generated so that = stylesheet_link_tag 'goodbye-application' will pick it up without the need to restart the production server.
My guess is that it has something to do to tell Rails to reload the public/manifest.json I just cannot figure it out
Rails 4.0.2
Ruby 2.1
Unicorn production server
My functionality is kinda similar to krautcomputing article however he is solwing this problem with "Digested" which is not working for rails 4 (+ it will be deprecated soon)
Update
One dude provided an answer (which he had remove) suggesting I don't need to do this because Unicorn have zero time restart, so I can just restart the unicorn and it will load the new public assets... That's true, I'm doing this this way before I've asked this question. However I'm looking for solution that avoid this. (let say users generate 1000 styles per day)
He also suggested to use Grunt... Well, that's plan B, I'm really wondering if there is a way to do this in Rails-sprockets :)
Why not just roll your own helper method?
def subdomain_application_stylesheet_link_tag(subdomain)
if Rails.env.production?
# This assumes you've precompiled the spreadsheet into the public directory via sprockets
stylesheet_link_tag File.basename(
Dir.glob("#{Rails.root}/public/assets/#{subdomain}-application-*.css").first
)
else
# Logic to development stylesheet selection
end
end
Here is the full solution to manifest reloading issue. As stated in the original post, the new manifest.json that you dynamically generate will not reload unless you fully reload your server (or kill works, etc). Just to reiterate:
If you are dynamically creating assets 'live', along with a manifest
to prevent caching, Sprockets doesn't reload the manifest between
requests.
I needed this manifest reloading capability for development only because I am rolling-my-own asset pipeline with Gulp for a performance boost. Basically, using this solution allows the assets to function exactly how they would in production, while still being able to live-reload them when developing with a new manifest...pretty slick, I hope you find it useful for your production needs.
create the file config/initializers/live-manifest.rb:
require 'sprockets/rails/helper'
module Sprockets
module Rails
module Helper
def asset_digest_path(path, options = {})
# create the new manifest, overriding sprockets' old one
if assets_manifest and manifest = Sprockets::Manifest.new(assets, Dir.glob(File.join(assets_manifest.dir, 'manifest-*.json')).first)
if digest_path = manifest.assets[path]
return digest_path
end
end
if environment = assets_environment
if asset = environment[path]
return asset.digest_path
end
end
end
end
end
end
Also, you're more than welcome to customize the new manifest directory if you desire. Mine is in the same place as the previous, with a different name, as you can see from the Dir.glob method call.

How to host a website on Rails without following the MVC structure?

This may be a dumb question, but since I don't know the answer I'm going to go ahead and ask anyway. I have an app (mobile) that is hosted on Heroku with a Rails server. I have all the basic API stuff there.
Now I have a landing page for my app that I would like to host on this server. It's a basic template, but the problem is that it has a non MVC structure, i.e it is just HTML, CSS and a few jQuery/JS plugins.
How do I host this on my existing server, which has an App --> Controllers, Models, Views, Assets... type of structure?
If you want to serve static pages in rails you can create a Static controller and Static views and route the actions in the Static controller to your Static views. Note there will be no associated static model. For example:
controllers/static_controller.rb
class StaticController < ApplicationController
def landing_page
end
def another_page
end
end
views/landing_page.html
<title> <h1>Landing Page stuff</h1> </title>
views/landing_page.html
<title> <h1>Another Static Page</h1> </title>
config/routes.rb
Project::Application.routes.draw do
root :to => "static#landing_page"
match '/another_page', to: 'static#another_page', via: 'get'
end
Firstly, if you want to do something out of scope of MVC, you may wish to check out other frameworks. IonicFramework recently just attracted funding & is an HTML5 mobile app framework
Static Pages
If your question is about loading static pages in Rails, you have to remember Rails is not tied into using a DB. The MVC structure is that - a structure
The Rails way of doing things is as such:
HTTP Request > ActionDispatch::Routing > Controller::Action
This means if you set up a route as follows:
#config/routes.rb
get "landing_page", to: "static#action"
get "static_page", to: "static#static_action"
You'll be able to see how to set up the controller from Steve's answer

Mount Sinatra app inside a rails app and sharing layout

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

How do you render the internal code documentation within a rails project

How do you make the documentation available after running rake doc:app available within your project.
I know I can browse on the local machine but I want to allow other uses to browse the documentation within the application.
I have created a documentation controller within which I have an index method as follows
class DocumentationController < ApplicationController
def index
render 'doc/app/index.html'
end
end
This partially works as it renders the frame but each frame is filled with messages such as
No route matches "/files/doc/README_FOR_APP.html" with {:method=>:get}
So how do I change where a particular controller sets its equivalent of DOCROOT to.
Why don't you copy 'doc' into the 'public' directory in the root of your rails app? They are just static HTML files and would be available through just Apache serving them.

Resources