Weird issue where I'm asking for the JS file, but Rails is serving the HTML file. And it's only on my staging server (Heroku) and not on my local machine.
I have a dynamic Javascript file which is something that needs to be included in other pages via a script to like so:
<script type="text/javascript" src="http://example.com/embed.js></script>
That embed maps to a controller and action which also handles HTML. The relevant route looks like this:
match "/embed(.:format)" => "articles#embed", as: "embed"
And the controller action is pretty standard.
def embed
respond_to do |format|
format.html do
#it renders some HTML
end
format.js #no block is given
end
end
And, I have two views under app/views/articles
embed.html.haml
embed.js.coffee
On my local machine, requesting localhost:3000/embed.js works. It renders the Javascript without a problem. However, on my staging server, here's what I see in the logs:
Started GET "/embed.js" for 124.168.219.36 at 2012-11-04 00:23:01 +0000
Processing by ArticlesController#embed as JS
Rendered articles/embed.html.haml (1.5ms)
Completed 500 Internal Server Error in 2ms
The Internal Server Error is not the issue. The issue is that it recognises the request as JS, yet decides to render the HTML template and only on staging.
What's going on?
Related
I am working on a page that shows user-uploaded videos and pictures. The path for this page is /users/:id/ and the pictures/videos are located in /public/media/users/[userName]/
user_controller:
def show
#user = User.find(params[:id])
posts = #author.posts
#pictures = posts.select(&:picture?)
#videos = posts.select(&:video?)
end
views/user/show.html.erb:
<% #pictures.each do |picture| %>
<%= image_tag("/" + picture.path) %>
<% end %>
<% #videos.each do |video| %>
<%= video_tag("/" + video.path) %>
<% end %>
The page loads all of the pictures/videos correctly. For example, one of the generated video tags looks like <video src="/media/users/user1/example.mp4"></video> which is perfect.
However, after loading the page Rails seems to issue an extra GET request for each of the videos.
Completed 200 OK in 126ms (Views: 13.7ms | ActiveRecord: 1.9ms | Allocations: 21392)
Started GET "/users//media/users/user1/vids/example.mp4" for ::1 at 2022-08-13 10:21:25 -0400
Started GET "/users//media/users/user1/vids/example2.mp4" for ::1 at 2022-08-13 10:21:25 -0400
Started GET "/users//media/users/user1/vids/example3.mp4" for ::1 at 2022-08-13 10:21:25 -0400
ActionController::RoutingError (No route matches [GET] "/users/media/users/user1/vids/example.mp4"):
ActionController::RoutingError (No route matches [GET] "/users/media/users/user1/vids/example2.mp4"):
ActionController::RoutingError (No route matches [GET] "/users/media/users/user1/vids/example3.mp4"):
Some observations:
This is only occurring for videos, no extra requests are generated for pictures
Rails seems to be prepending the controller name to the start of these paths
Why are these requests being issued?
First, I have no idea why the requests are happening twice, I suspect Rails is either trying to speed up page loads by delaying loading the actual videos until after the page has rendered or it is making the second request to try and get a :poster value from the video object (see docs for video_tag)
But, I think this is probably intended behavior and the problem isn't that the 2nd GET request is happening, the problem is that the 2nd GET request has a bad URL.
I think I have an idea why the 2nd call's URL is bad.
My thought is that Rails doesn't have all the [userName] folders added to the asset pipeline.
Hence why you need to have the '/' before the picture and video path.
But, because the folders aren't in the pipeline, they are being resolved by just appending the generated video route to the current route, hence why you are seeing two backslashes together in the 2nd GET calls.
IF these folders are dynamically created, adding them to the asset pipeline without restarting the app is beyond me, but probably possible with some meta Rails programming.
If these folders are static, or the app can be restarted after a new folder is created, you can make sure they get added to the asset pipeline:
First, see if these folders are in the asset pipeline:
You can view the search path by inspecting Rails.application.config.assets.paths in the Rails console.
If they aren't in there, it might be because you've put them in /public but haven't enabled public assets:
In previous versions of Rails, all assets were located in subdirectories of public such as images, javascripts and stylesheets. With the asset pipeline, the preferred location for these assets is now the app/assets directory. Files in this directory are served by the Sprockets middleware.
Assets can still be placed in the public hierarchy. Any assets under public will be served as static files by the application or web server when config.public_file_server.enabled is set to true. You should use app/assets for files that must undergo some pre-processing before they are served.
# application.rb
module YourAppName
class Application < Rails::Application
...
config.public_file_server.enabled = true
...
end
end
or this behavior can be customized per environment:
# production.rb, development.rb, or test.rb
Rails.application.configure do
...
config.public_file_server.enabled = true
...
end
If this still doesn't fix it, you might have to manually add public/media/users/* to the pipeline like this.
Recently, I've upgraded a Rails app that I'm maintaining to Rails 6 RC2 (coming from 5.2.3). So, right after upgrading, I ran the automated tests (RSpec) and the test output gave me lots of deprecation warnings. One of those warnings was:
DEPRECATION WARNING: render file: should be given the absolute path to a file
So I went to the view file that triggered the warning, and made the changes as follows,
Before:
render file: 'devise/sessions/new'
After:
render file: Rails.root.join('app', 'views', 'devise', 'sessions', 'new.html.slim')
I ran the tests again, no output of deprecation warnings was seen. However, after switching to absolute paths, the view is now only rendering plain HTML code but if I remove the .slim extension, i.e.
render file: Rails.root.join('app', 'views', 'devise', 'sessions', 'new.html')
The corresponding view is rendered properly but now the test will complain about not using absolute paths. Is there a way to fix this or is this a Rails/Slim bug?
In your case it looks like you want to render a normal view, i.e. a template.
In that case using the file option is not the recommended way. Instead you should be using the template option.
render template: 'devise/sessions/new'
Or even better, you can use this shortcut:
render 'devise/sessions/new'
Background
The file option is intended to render a view which is outside your Rails application, where you can't rely on Rails' view lookup logic. Consequently Rails wants to have an absolute path. Demanding an absolute path also forces the developer to think about relative path segments (/../).
Omitting the .slim extension and then having the file processed by the template engine is a feature intended for templates. Using file seems to provide the very same functionality, but my guess is that this is just a side effect of the internal workings of the view path lookup. It looks like the Rails developers want to improve the distrinction between files and templates in the future and deprecating relative files is an intermediate step to not break too many existing applications which rely on using file and still expect the features of a template.
PS: It is not necessary to manually split your path. So if you for some reason still want to use file with an absolute path, instead of
render file: Rails.root.join('app', 'views', 'devise', 'sessions', 'new.html.slim')
use this
render file: Rails.root.join('app/views/devise/sessions/new.html.slim')
I had this same challenge when working on Rails 6 API-only application.
I wanted to render a static page from a controller called home_controller.rb
Here's my code:
require 'rails/application_controller'
class HomeController < Rails::ApplicationController
def index
render file: Rails.root.join('public/index.html')
end
end
But when I try accessing the page I get the error:
Started GET "/favicon.ico" for ::1 at 2021-02-23 16:25:41 +0100
Processing by HomeController#index as */*
Parameters: {"other"=>"favicon"}
Completed 500 Internal Server Error in 1ms (ActiveRecord: 0.0ms | Allocations: 301)
ArgumentError (`render file:` should be given the absolute path to a file. '/home/my-computer/Projects/MyWebsite/public/index.html' was given instead):
Here's how I solved it:
The issue was that I was missing the file index.html in the directory public, so Rails could not locate it.
All I had to do was to add the file index.html in the directory public. This time when I tested it was fine.
That's all.
I hope this helps
I have a situation in Rails 5.2 where a controller is rendering a .js.erb file to the view literally, i.e. showing the javascript code in the view itself.
How can I get Rails to run the js.erb file instead of showing it as if it were a DOM file? It is taking the user away from a page I want the user to stay on.
# controller
def submit_custom_data
...perform some functions...
respond_to do |format|
format.js
end
end
# submit_custom_data.js.erb
console.log('submit_custom_data...');
... Other javascript ...
Additional Info
By preserving the browser console logs on Chrome, I see this:
Resource interpreted as Document but transferred with MIME type text/javascript: "http://localhost:3000/submit_custom_data/1068.js?c=1&d=10&db=1&s=8&tm=&w=3".
Navigated to http://localhost:3000/submit_custom_data/1068.js?c=1&d=10&db=1&s=8&tm=&w=3
This issue did NOT occur on Rails 4.2.7. After updating to Rails 5.2.7 (and updating the bundle), I'm now seeing these issues.
I scoured my app's directories, and I can't find the html page for the default rails Welcome Aboard page. I also cannot find a route for the default Welcome Aboard page in routes.rb. How does my rails app route http://localhost:3000/ to a non-existent page in my app?
The rails server produces this information:
Started GET "/" for 127.0.0.1 at 2013-07-31 02:00:13 -0600
Processing by Rails::WelcomeController#index as HTML
Rendered /Users/7stud/.rvm/gems/ruby-2.0.0-p247#railstutorial_rails_4_0/gems/railties-4.0.0/lib/rails/templates/rails/welcome/index.html.erb (0.1ms)
Completed 200 OK in 3ms (Views: 2.5ms | ActiveRecord: 0.0ms)
So it looks to me like there is a controller buried in a gem somewhere that handles the request.
Since Rails 4, the "Welcome aboard" page is no longer located in public/index.html. It is - as you've already detected - located inside one of the Rails gems.
So you already answered the question yourself; the "Welcome aboard" page is - in your case - located at /Users/7stud/.rvm/gems/ruby-2.0.0-p247#railstutorial_rails_4_0/gems/railties-4.0.0/lib/rails/templates/rails/welcome/index.html.erb
To get rid of it, following the instructions on the page. Basically they are:
Create a controller
Add a root route in config/routes.rb to route to that newly created controller.
As for how the request to your application ends up at a controller inside railties, let's dig into the gem: Inside Rails::Application::Finisher we find this:
initializer :add_builtin_route do |app|
if Rails.env.development?
app.routes.append do
get '/rails/info/properties' => "rails/info#properties"
get '/rails/info/routes' => "rails/info#routes"
get '/rails/info' => "rails/info#index"
get '/' => "rails/welcome#index"
end
end
end
This block adds a few routes to your application when running in development mode - one of those is the route to the "Welcome aboard" action: get '/' => "rails/welcome#index"
This - like any other initializer - is done when your start your application server (running rails server or however you do it). In the case of Finisher, all its initializer are run after all other initializers are run.
Note how the routes are appended so that they are appear last in the Routeset. This, combined with the fact that Rails uses the first matching route it finds, ensures those default routes will only get used if no other route is defined.
This one's got me stumped.
I've got a ruby on rails site here (rails 2.0.2, ruby 1.8.6, running on thins (0.8.2) sitting on top of nginx), and when I hit a certain action I sometimes get the correct response, other times I get a 500 error.
The code in this view goes as follows:
def show
#item = Item.find_by_url_slug(params[:id])
filename = "#{RAILS_ROOT}/public/#{#item.index.url}"
#data = File.open(filename, 'rb') { |f| f.read } # read data via File.
render :layout => false
end
The view is rather simplistic, with show.html.erb containing
<%= #data %>
I've suspected caching, and have tried setting a variety of different headers (cache-control, expires etc), and also tried the expires_now() method, to no avail.
Out of interest I've also tried adding a sleep(2) call in the controller, and looking at the network requests it's obvious on the 500 pages that this method isn't being called - none of the above code in the show action is.
Furthermore, there's no evidence of the call in the production.log when the page doesnt load. So, no errors to debug, just the occasional 500 (happening say 2/4 page loads).
I should add that this code works fine locally, and on our staging server - it's only in production that this is an issue.
Any ideas??