I've set up a local Rails app that uses Turbo. I'm trying to shim the Turbo Demo iOS app to display my local Rails app. Has anyone been able to do a similar thing?
I switched Demo.current to use the local address, but all the requests show the "Error loading page" screen, with no useful logs coming out of the Demo app.
The Rails app shows my base route as being attempted, with some form of persistence with retrying a 401 error. I changed the SceneController default URL to load a /networks/all-people, which requires an authorized user (via Devise) to hopefully see how the authentication logic would go. Below is the Rails output when running the Demo app:
Started GET "/networks/all-people" for 127.0.0.1 at 2021-01-21 19:50:26 -0500
Processing by NetworksController#all_people as HTML
Completed 401 Unauthorized in 0ms (Allocations: 256)
Started GET "/users/sign_in" for 127.0.0.1 at 2021-01-21 19:50:26 -0500
Processing by Devise::SessionsController#new as HTML
Rendering layout layouts/application.html.erb
Rendering devise/sessions/new.html.erb within layouts/application
Rendered devise/shared/_links.html.erb (Duration: 0.3ms | Allocations: 218)
Rendered devise/sessions/new.html.erb within layouts/application (Duration: 1.7ms | Allocations: 1056)
Rendered layout layouts/application.html.erb (Duration: 9.4ms | Allocations: 7318)
Completed 200 OK in 10ms (Views: 9.8ms | Allocations: 7945)
Has anyone been able to shim the Demo app into working with a local Rails server? I'm entirely unsure of what's happening wrong here, or whether shimming is even a good idea here.
Turbo is being loaded in, as verified by the following event listener fires when visiting from my browser.
<script>
document.addEventListener("turbo:load", function(e) {
console.log("TURBO LOADED");
});
</script>
There are a few things that are needed to get Devise to work with Turbo.
Add app/controllers/turbo_controller:
# frozen_string_literal: true
class TurboController < ApplicationController
class Responder < ActionController::Responder
def to_turbo_stream
controller.render(options.merge(formats: :html))
rescue ActionView::MissingTemplate => e
raise e if get?
if has_errors? && default_action
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
else
redirect_to navigation_location
end
end
end
self.responder = Responder
respond_to :html, :turbo_stream
end
Add data: { turbo: "false" } to your devise login form:
# app/views/devise/sessions/new.html.erb
<%= form_for(resource, as: resource_name, url: session_path(resource_name), data: { turbo: "false" }) do
Update config/initializers/devise.rb:
class TurboFailureApp < Devise::FailureApp
def skip_format?
%w[html turbo_stream */*].include?(request_format.to_s)
end
end
config.parent_controller = "TurboController"
config.navigational_formats = ["*/*", :html, :turbo_stream]
config.warden do |manager|
manager.failure_app = TurboFailureApp
end
Detailed explanation thanks to GoRails!
Related
So I have a 'ThaaliTakhmeens' controller where some actions have their corresponding turbo_stream templates to lazy load instances from databases using hotwire with pagination (pagy). And in those actions, there's a similar logic that I want to factor out into an after_action callback, (following the DRY principle).
After factoring out the code into an after_action, the instances don't show up on the page in fact the after_action doesn't get executed at all which I verified by giving the debugger in it. I have also provided a before_action to those actions which works perfectly fine.
Here's the code:
after_action :set_pagy_thaalis_total, only: [:complete, :pending, :all]
def complete
#tt = ThaaliTakhmeen.includes(:sabeel).completed_year(#year)
end
def pending
#tt = ThaaliTakhmeen.includes(:sabeel).pending_year(#year)
end
def all
#tt = ThaaliTakhmeen.includes(:sabeel).in_the_year(#year)
end
private
def set_pagy_thaalis_total
#total = #tt.count
#pagy, #thaalis = pagy_countless(#tt, items: 8)
debugger
end
Here's the log on visiting the 'complete' action:
Started GET "/takhmeens/2022/complete" for ::1 at 2023-01-21 10:07:35 +0530
Processing by ThaaliTakhmeensController#complete as HTML
Parameters: {"year"=>"2022"}
Rendering layout layouts/application.html.erb
Rendering thaali_takhmeens/complete.html.erb within layouts/application
Rendered shared/_results.html.erb (Duration: 2.4ms | Allocations: 2088)
Rendered thaali_takhmeens/complete.html.erb within layouts/application (Duration: 3.7ms | Allocations: 2396)
Rendered layout layouts/application.html.erb (Duration: 3.9ms | Allocations: 2477)
Completed 500 Internal Server Error in 6ms (ActiveRecord: 0.0ms | Allocations: 3027)
ActionView::Template::Error (undefined method `any?' for nil:NilClass
'.freeze; if instances.any?
^^^^^):
1: <%= turbo_frame_tag :results, data: { turbo_action: "advance" } do %>
2: <div class="container text-center mt-5">
3: <% if instances.any? %>
4: <%= render partial: "theader" %>
5: <div id=<%="#{id}"%> ></div>
6: <%= turbo_frame_tag :pagination, loading: :lazy, src: path %> %>
app/views/shared/_results.html.erb:3
app/views/shared/_results.html.erb:1
app/views/thaali_takhmeens/complete.html.erb:8
Since the after_action callback doesn't run, the instances (#thaalis) object is not set hence this error shows up, and also debugger didn't get executed either.
Here the complete action has both HTML and turbo_steam templates. And just to be clear that the content loads perfectly fine without the need for an after_action callback but that would be against the DRY principle.
So what would be the workaround for this? Is there another way to refactor the code or should I have to set something explicitly in the callback method to get it executed?
Good question. I actually not use after_action often and had to check. https://guides.rubyonrails.org/action_controller_overview.html#after-filters-and-around-filters What I think happen is that the rendering of the view is part of the action.
In your case this is inferred, you have no respond to block like this:
respond_to do |format|
if #record.update(record_params)
format.html { redirect_to a_record_route }
else
format.html { render :edit, status: :unprocessable_entity }
end
end
But the rendering of the template still happens in the action. Before you set some instance variables useful to the view.
What you can do if you really want to dry up your controller is adding set_pagy_thaalis_total at the end of each of your actions and removing the after_action.
EDIt: Also the fact your view is an html.erb or turbo_stream.erb file doesn't really matter.
In very short.
When I have a form set as POST, the controller picks up the request, processes it and even starts to render the correct view. But the browser stays on the form page
When I switch the form to GET, it works
(yes, I remembered to change the route from get to post and back)
Here is the log:
Started POST "/sooth/search" for 127.0.0.1 at 2022-07-02 13:43:40
-0700 Processing by SoothController#search as TURBO_STREAM Parameters: {"authenticity_token"=>"[FILTERED]",
"search"=>{"name"=>"search keywords"}, "commit"=>"Save Search"}
Rendering layout layouts/application.html.erb Rendering
sooth/search.html.erb within layouts/application
I am rendering the search html page
The line above is a log message in the search.html.erb page
Rendered sooth/search.html.erb within
layouts/application (Duration: 1.0ms | Allocations: 148) Rendered
layout layouts/application.html.erb (Duration: 6.6ms | Allocations:
2710) Completed 200 OK in 15ms (Views: 11.3ms | ActiveRecord: 0.0ms |
Allocations: 4040)
BUT the search page is not displayed. Browser stays on the search form page.
Any hints deeply appreciated.
(And as you have probably guessed, I am day 1 with rails)
EDIT:
class SoothController < ApplicationController
include SoothHelper
def index
puts "sooth index"
template = get_query_template('sooth_search')
puts(template)
end
def search
form_params = params[:search]
puts 'searching' + form_params[:name].to_s
render "sooth/search"
end
end
ROUTES
Rails.application.routes.draw do
Rails.application.routes.draw do
get "/nlp_data", to: "nlp_data#index"
get "/sooth", to: "sooth#index"
post "/sooth/search", to: "sooth#search"
end
Your problem is you are trying to render the same page instead of redirecting to to the sooth page ,and secondly you cannot acces params directly in a post request, instead you must acces it from a strong param method
class SoothController < ApplicationController
include SoothHelper
def index
puts "sooth index"
template = get_query_template('sooth_search')
puts(template)
end
def search
form_params = sooth_params[:search]
puts 'searching' + form_params[:name].to_s
redirect_to "/sooth"
end
private
def sooth_params
params.require(:sooth).permit(:search)
end
end
I have a form being submitted by the resident of a condominium to apply a pass for a visitor.
Upon submission I have setup the controller to render a template or redirect it to another path depending on the input of the form and display a flash message on the top of the page after clicking the submit button.
Somehow redirect works fine but render does nothing to the page.
Did try flash.now with render but no flash and it looks like it is not loading anything new on the page.
class VisitorPassesController < ApplicationController
def new
#visitor_pass = VisitorPass.new
#controller = "visitor_passes"
end
def create
unless resident && correct_resident_key?
flash[:danger] = "Invalid resident key."
# render 'new' doesn't work
# render action: 'new' doesn't work
redirect_to new_visitor_pass_path works
redirect_to '/visitor_passes/new' works
return
end
.
.
.
end
end
Here is the output of the console.
Rendering visitor_passes/new.html.erb within layouts/application
Rendered visitor_passes/new.html.erb within layouts/application
Rendered layouts/_rails_default.html.erb
Rendered layouts/_shim.html.erb
Resident Load (0.3ms) SELECT "residents".* FROM "residents" WHERE "residents"."id" = ? LIMIT ? [["id", 155], ["LIMIT", 1]] app/helpers/sessions_helper.rb:17:in 'current_user'
Rendered layouts/_header.html.erb (Duration: 3.1ms | Allocations: 760)
Completed 200 OK in 429ms (Views: 59.6ms | ActiveRecord: 2.3ms | Allocations: 33936)
Flash messages are displayed in the next response cycle (ie after a redirect). If you want to display a flash message in response to the current request use ActionDispatch::Flash::FlashHash#now.
class VisitorPassesController < ApplicationController
def new
#visitor_pass = VisitorPass.new
# just use the controller_name method provided by rails instead
end
def create
# ...
# prefer positive conditions instead of negative
if resident && correct_resident_key?
# do something awesome
else
flash.now[:danger] = "Invalid resident key."
render :new
end
end
end
Also make sure you are using the local: true option on the form if using form_with as it defaults to those pesky XHR remote: true requests.
when I run RSpec, I 've got some unwanted controller / routing logs I want to get rid of.
How do I get rid of those?
rspec
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
Randomized with seed 61970
....Processing by Api::CommentsController#update as HTML
Parameters: {"comment"=>{"content"=>"Attempt to edit comment."}, "id"=>"28"}
Filter chain halted as :find_comment rendered or redirected
Completed 401 Unauthorized in 20ms (ActiveRecord: 4.4ms)
.Processing by Api::CommentsController#update as HTML
Parameters: {"comment"=>{"content"=>"Editing is nice"}, "id"=>"29"}
Completed 200 OK in 22ms (Views: 0.6ms | ActiveRecord: 5.3ms)
.Processing by Api::CommentsController#update as HTML
Parameters: {"comment"=>{"content"=>"Attempt to edit someone else's comment."}, "id"=>"30"}
Filter chain halted as :find_comment rendered or redirected
Completed 401 Unauthorized in 14ms (ActiveRecord: 3.3ms)
.Processing by Api::CommentsController#create as HTML
Parameters: {"comment"=>{"subject_id"=>"201", "subject_type"=>"Card", "content"=>"This is so useful!"}}
Completed 200 OK in 41ms (Views: 0.6ms | ActiveRecord: 9.2ms)
1.You can write a filter like this:
def silence_action
Rails.logger.silence do
yield
end
end
And add following line at your rspec so that your logger gets silenced:
RSpec.describe BaseController, :type => :controller do
controller.class.around_filter :silence_action, :only => :action
end
2.Another way of silencing your logger would be writing a custom logger:
# lib/custom_logger.rb
class CustomLogger < Rails::Rack::Logger
def initialize app, opts = {}
#app = app
#opts = opts
#opts[:silenced] ||= []
end
def call env
if #opts[:silenced].include?(env['PATH_INFO']) || #opts[:silenced].any? {|silencer| silencer.is_a?( Regexp) && silencer.match( env['PATH_INFO']) }
Rails.logger.silence do
#app.call env
end
else
super env
end
end
end
Then at your rspec you can call a function that is responsible for switching logger:
require File.dirname(__FILE__) + '/../lib/custom_logger.rb'
def use_custom_logger
Rails.application do
config.middleware.swap Rails::Rack::Logger, CustomLogger, :silenced => ["/noisy/action.json"]
end
end
I've got a Rails app which generally works fine, but one (that I can find) user is causing a 302 error, and they can't log in. The log looks like this:
Started GET "/d/sign_in" for 127.0.0.1 at 2014-12-15 05:38:14 +0000
Processing by Devise::SessionsController#new as */*
Rendered devise/sessions/new.html.erb within layouts/application (0.5ms)
Rendered layouts/_navigation.html.erb (2.3ms)
Rendered layouts/_messages.html.erb (0.1ms)
Completed 200 OK in 10.1ms (Views: 7.2ms | ActiveRecord: 0.8ms)
Started GET "/d/sign_in" for 127.0.0.1 at 2014-12-15 05:38:14 +0000
Processing by Devise::SessionsController#new as */*
Rendered devise/sessions/new.html.erb within layouts/application (0.7ms)
Rendered layouts/_navigation.html.erb (2.2ms)
Rendered layouts/_messages.html.erb (0.2ms)
Completed 200 OK in 10.9ms (Views: 8.1ms | ActiveRecord: 0.7ms)
Started GET "/" for 58.111.229.203 at 2014-12-15 05:38:20 +0000
Processing by DocumentsController#index as HTML
Completed 401 Unauthorized in 0.5ms
Started POST "/d/sign_in" for 58.111.229.203 at 2014-12-15 05:38:28 +0000
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"cvyg6GWTtpd4M1klk0j6APbv4h36+a99yb9k646BRZA=", "user"=>{"email"=>"admin#blank.net", "password"=>"[FILTERED]"}, "commit"=>"Sign in"}
Redirected to http://appdomain.com/
Completed 302 Found in 310.2ms (ActiveRecord: 0.0ms)
Started GET "/d/sign_in" for 127.0.0.1 at 2014-12-15 05:38:29 +0000
Processing by Devise::SessionsController#new as */*
Rendered devise/sessions/new.html.erb within layouts/application (0.5ms)
Rendered layouts/_navigation.html.erb (2.7ms)
Rendered layouts/_messages.html.erb (0.1ms)
Completed 200 OK in 10.1ms (Views: 7.4ms | ActiveRecord: 0.8ms)
Now, the 127.0.0.1 things concern me since this is a production environment, but it might be just a service (Pingdom) ensuring the app is still up. Nevertheless, this user can't log in and I can't figure it out. No other users are affected that I know of, and the user has everything they need to be able to log in. No detailed errors are in the log (like missing resources or similar), it just hangs when they log in. Any help would be great.
Update
Here's DocumentsController (the relevant parts):
class DocumentsController < ApplicationController
include ApplicationHelper
include DocumentsHelper
include ServerHelper
load_and_authorize_resource
def index
#documents = current_user.documents.includes(:template).includes(:user)
.includes(:pdf_result).created.page(params[:page])
.per(10)
#categories = current_user.brand.templates.all.group_by(&:category)
#assets = AssetResources.new(current_user)
end
...
Changing this user to an administrator does not fix the problem. I guess ApplicationController is relevant too, and this is it:
class ApplicationController < ActionController::Base
before_filter :authenticate_user!, :check_mode
protect_from_forgery
rescue_from CanCan::AccessDenied do |exception|
redirect_to documents_url, alert: exception.message
end
helper_method :current_user, :authorised_user
hide_action :current_user
def mode
#mode = Mode.first
end
def check_mode
flash.now[:alert] = mode.messages unless mode.all_online
end
private
def user_activity
current_user.try :touch
end
def authorised_user
#authorised_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
UPDATE The credentials are correct, when that email address and an incorrect password are entered I get a bad credentials message. The correct credentials just hangs.
Check
Whether this "admin#blank.net" user exists in database.
You are providing correct credentials.
Because when sign in fails, devise internally performs redirection and 302 is HTTP response status code for URL redirection.
In the database, ensure whether this user exists and you are providing the right credentials.
I know this is old. But just in case someone has the same problem, as I just did, here is what fixed it for me:
In
config/initializers/session_store.rb
change
Rails.application.config.session_store :cookie_store, key: 'my_secure_session', httponly: false, secure: true
to
Rails.application.config.session_store :cookie_store, key: 'my_session'
Why I had this problem in the first place: I copied a running Rails 4 server with SSL and booted it up in dev mode without SSL. Commenting out force_ssl in application_controller.rb allowed me to start the server without ssl but left me with the 302 and 401 error and a redirect back to the sign-in page (without notification).