Ruby on Rails - Redirect/render via action does not work - ruby

I have the following code:
def isLoggedIn
if session[:username].nil?
puts "Not logged in!"
return render :controller=>"admin", :action=>"login"
end
end
And the following code which calls this action at the top to check the status of the user.
def view_gallery
isLoggedIn
.
.
.
.
\/
return render "view_gallery"
end
The problem I am having is that the code continues to execute past isLoggedIn although the console is logging "Not logged in!" because as expected the user isn't logged in. This should then render an alternative layout which it doesn't do. If I mispell the action (say "logib") then it complains about a missing template - so I can see it running the render function.
The same occurs if I change render to redirect_to and/or if I move the return statement to the end of the line.
Any idea's?
Many thanks

The Rails Guides says to do this in the following way:
render :controller=>"admin", :action=>"login" and return

Related

Is there a way to abort a test cleanly?

Using Selenium, Ruby
I'm trying to learn the correct way of closing out a test should an object not exist. For example I have a test that calls a function "Click_Login" which in turn goes to the function and returns the object reference or the text "Stop Test" if it does not exist. That part is working correctly.
However after the browser is closed, the test continues on and tries to varLoginBtn.click and fails because Stop Test.click does not exist. I thought the test would have stopped after the driver.quit and not continue on to varLoginBtn.click.
My goal is to have the test stop cleanly if an object does not exist. I maybe doing this incorrectly.
#The test.rb
varLoginBtn = toNavigate.Click_LogIn(driver) #calls function down below.
if varLoginBtn == "Stop Test"
driver.quit
end
varLoginBtn.click #click on the button
#functions.rb
#in the Class toNavigate
#Login Button
def Click_LogIn(driver)
puts "In the login"
wait = Selenium::WebDriver::Wait.new(:timeout => 15)
begin
element= wait.until{driver.find_element(:xpath, "//*[#xas-string='SDE_LOG_INN']")} #Log_INN is intentional I want it to fail.
return element
rescue
puts "The Login button did not exist"
return "Stop Test"
end
end
Thanks for your help.
You don't need to rescue, you have a condition if nil and you can use abort to exit script with a message
But also use snake_case for def ruby methods
def click_login(driver)
puts "In the login"
wait = Selenium::WebDriver::Wait.new(:timeout => 15)
if element = wait.until{driver.find_element(:xpath, "//*[#xas-string='SDE_LOG_INN']")} #Log_INN is intentional I want it to fail.
return element
else
puts 'The Login button did not exist'
abort 'Stop Test'
end
end
This is an overengineered way to do this... why not just throw an exception when this occurs and let the test die with a good message? Something like
...
rescue
raise "The Login button did not exist"
end
Your test library should be able to handle this and print a nice message that you can use to investigate, etc.
Read more about exceptions in ruby here.

Rails 4 Close current tab from controller

So, I have this html.erb and this controller (shown below). What I want is to, if simple_captcha.valid? to increment reports, save, AND close current tab. I want to do it from controller, if possible! (And also, would it be a good practice?)
I saw several examples of this done on view page using javascript, but I know nothing of javascript and, if possible, I'd like to deal with it on controller. But, in case I really have to learn javascript to achieve what I want, which direction should I take?
#view (html.erb)
<h4>To report, complete captcha</h4>
<%= show_simple_captcha %>
<%= button_to "report post", create_report_post_path(#forum_post.id) %>
-----------------------------------
#controller
def new_report_post
#forum_post = ForumPost.find(params[:id])
end
def create_report_post
#forum_post = ForumPost.find(params[:id])
if simple_captcha_valid?
#forum_post.reports += 1
#forum_post.save
redirect_to ???
flash[:success] = "Mandou ver."
else
redirect_to report_post_path
flash[:warning] = "Captcha inválido."
end
end
I don't think, you can close a tab, unless that tab was explicitly opened by javascript.
You can refer to this question: link
If however, you are opening the view using javascript. you can send window.close() using a js.erb view.
Instead of redirect_to, it would be something like
respond_to do |format|
format.js { render "js_erb_view" }
end
Inside your js.erb view file,you can send
window.close()
This would only work if you send an ajax request. One of the possible solutions to make this work :)

Capybara and Rails have_content failing in spec to test to see if a messages partial appears

I have a request spec which passes up until the point where I need to check to see if content is present on the page, which I am using page.should have_content to do. The content is actually a message which appears on successful form submission, which is rendered from a messages partial. The test fails even though If I test through the browser, functionality works as expected and the content appears as it should. I'm also using FactoryGirl to generate the users to use for the form submission.
Here's the error I get after running the spec with the --format d option:
UserSignup
shows a thank you message on successful form submission (FAILED - 1)
Failures:
1) UserSignup shows a thank you message on successful form submission
Failure/Error: page.should have_content("Thank you. You will be notified of our launch at #{user.email}.")
expected #has_content?("Thank you. You will be notified of our launch at quinn.purdy#casperzboncak.org.") to return true, got false
# ./spec/requests/user_signup_spec.rb:21:in `block (2 levels) in <top (required)>'
user_signup_spec.rb:
require 'spec_helper'
describe "UserSignup" do
it "shows a thank you message on successful form submission" do
user = FactoryGirl.create(:user)
visit sign_up_path
fill_in "user_fullname", with: user.fullname
fill_in "user_email", with: user.email
click_button "Sign up"
current_path.should eq(sign_up_path)
page.should have_content("Thank you. You will be notified of our launch at #{user.email}.")
end
end
users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(secure_params)
if #user.valid?
#user.subscribe
flash[:notice] = "Thank you. You will be notified of our launch at #{#user.email}."
redirect_to sign_up_path
else
render :new
end
end
private
def secure_params
params.require(:user).permit(:fullname, :email)
end
end
I'm wondering if it could be because I render the messages partial from the application layout, but when it gets outputted in the users view, the message appears inside the body of the source, but outside the main class.
So I seem to have got the tests passing by adding the line , :js => true do beside 'it' and using the selenium web driver. There's got to be a way to do it without selenium I'm thinking, because you have to sit and wait while it actually runs it in a browser which is the downside.
Maybe I'm going about it the wrong way, and I should actually be checking for the partial in a view spec (currently it was just part of the feature test).

Receiving AbstractController::DoubleRenderError when using authenticate_or_request_with_http_basic()

I have a working controller action that breaks when I add a call to authenticate_or_request_with_http_basic at the beginning of the block. Here is the non-functioning action code:
def index
authenticate_or_request_with_http_basic do |username, password|
username == "test1" and password == "test"
end
render layout: false, content_type: 'application/xml'
end
I am receiving a "AbstractController::DoubleRenderError" error response in my browser window with the following message:
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
When I place the "authenticate_or_request_with_http_basic" logic in a separate action and then configure a before_filter to run it for the index action, all is well. However, I'm not reusing this logic for any actions other than index, and I'd like to know why the above code doesn't work.
Solution
I was able to find a solution with the help of Tyler and RKB. Here it is:
authenticated = authenticate_or_request_with_http_basic "Authentication Required" do |username, password|
#user = User.find_by_username(username)
#user != nil && password == "test"
end
return unless authenticated == true
authenticate_or_request_with_http_basic returns "true" if authentication was successful. It returns a 401 on failure.
It sounds like authenticate_or_request_with_http_basic is either render or redirecting. I'm guessing it works when you move it to a before_filter because it returns false which cancels the callback chain and causes the 2nd render to not occur? Just a guess...
authenticate_or_request_with_http_basic calls render to send the HTTP response needed to do HTTP basic authentication (status code 401 Unauthorized). See the Rails code for the details.

Accessing Sinatra scope from another class

I'm running a Sinatra application with a few extra classes pulled in for creating a User and a few others on the fly (no DB, it feeds in from a web service). I'm trying send out a flash notice (using https://github.com/nakajima/rack-flash) from within my User model but can't figure out how to get access to the flash method/variable because I'm out of scope.
Something like:
class User
def something
if true
flash[:notice] = 'Good job'
else
# nope
end
end
end
Which gets required into the Sinatra app by a simple require 'models/user'
This is an XY Problem[1]. Sinatra is responsible for sending out flash messages, not your User objects, so the code for setting the flash should be in your Sinatra app, not in your User class.
[1] http://www.perlmonks.org/index.pl?node_id=542341
You should not ask your User (model) to talk to the UI (view). That's bad/not MVC-clean. That's what a controller is for.
You can use either return values, exceptions, or throw/catch (which is not exception handling) to pass information from your model to your controller. For example, using return values:
post "/create_user" do
flash[:notice] = case User.something
when User then "User Created!"
when :nono then "That's not allowed"
when :later then "User queued to be created later."
end
end
class User
def self.something
if authorized
if can_create_now
new(...)
else
queue_create(...)
:later
end
else
:nono
end
end
end
Since I mentioned them above, following are examples using throw/catch and begin/rescue (exceptions). As the advisability of using either of these constructs is questionable, let us take a moment of silence to ponder if this is a good idea.
Here is an example using throw/catch:
post "/create_user" do
result = catch(:msg){ User.something }
flash[:notice] = case
when :nono then "That's not allowed"
when :later then "User queued to be created later."
else "User Created!"
end
end
class User
def self.something
throw :msg, :nono unless authorized
if can_create_now
new(...)
else
queue_create(...)
throw :msg, :later
end
end
end
Finally, here's an example using exceptions, though I'm not convinced that this will be appropriate for all (non-disastrous) cases where you want to flash unique messages to the user:
post "/create_user" do
flash[:notice] = "User Created!" # Assume all good
begin
User.something
rescue User::Trouble=>e
flash[:notice] = case e
when Unauthorized then "That's not allowed"
when DelayedCreate then "User queued to be created later."
else "Uh...Something bad happened."
end
end
end
class User
class Trouble < RuntimeError; end
class Unauthorized < Trouble; end
class DelayedCreate < Trouble; end
def self.something
raise Unauthorized unless authorized
if can_create_now
new(...)
else
queue_create(...)
raise DelayedCreate
end
end
end
Exceptions let you pass an additional data along (e.g. raise Unauthorized.new "No such account", or any custom properties you want to add to your class), and so may be more useful (when appropriate). Just remember to pass semantic results from your model to your controller, and let it translate them into user-facing messages.

Resources