Access OmniAuth helper in Sinatra view - ruby

I'm new to this, so forgive me if I'm being stupid!
I'm using OmniAuth for Sinatra to help authenticate users. In my layout.erb, I'd like to access the current_user helper to check if the user is logged in. The helper goes like this:
def current_user
#current_user ||= User.get(session[:user_id]) if session[:user_id]
end
But I'm not sure how to access it in my layout view. I thought this would do the trick:
<% if current_user %> Do stuff here <% end %>
But no luck. Any help would be appreciated! Like I said, I'm new to Ruby, and I'm not a strong developer.

You need to define that method inside helpers block, like this:
helpers do
def current_user
#current_user ||= User.get(session[:user_id]) if session[:user_id]
end
end
Then it will be available to the view.

Related

How to map routes to controllers in Sinatra?

I'd like to create a simple experimental MVC framework using Sinatra.
I'd like to define resources by name "pages" for example should resolve to:
/pages (index)
/pages/new
/pages/:id/show (show)
as WELL as map to app/controllers/PagesController.rb with corresponding get('/') to be responsible for the index, post('/pages/create') be responsible for creation, etc.
Trouble is even after reading the official documentation I'm terribly confused. I imagine I need to use non-classic Sinatra model for this, but could anyone point me in the right direction?
Thank you
If you want what I think you're wanting, I do this all the time. Initially for this scheme I used the travis-api source as a reference, but essentially what you want to do is extend Sinatra::Base in a "controller" class and then mount up your individual Sinatra "controllers" in rack, something like this:
module Endpoint
def self.included(base)
base.class_eval do
set(:prefix) { "/" << name[/[^:]+$/].downcase }
end
end
end
class Users < Sinatra::Base
include Endpoint
get '/' do
#logic here
end
get '/:id' do
#logic here
end
post '/' do
#logic here
end
patch '/:id' do
#logic here
end
end
class Posts < Sinatra::Base
include Endpoint
post '/' do
#logic here
end
end
and then something like this:
class App
require "lib/endpoints/users"
require "lib/endpoints/posts"
attr_reader :app
def initialize
#app = Rack::Builder.app do
[Users, Posts].each do |e|
map(e.prefix) { run(e.new) }
end
end
end
def call(env)
app.call(env)
end
end
You can adjust this to whatever you need, but the idea is the same, you separate your app into composable Sinatra applications that each have a prefix that they are mounted under using Rack. This particular example will give you routes for:
get '/users'
get '/users/:id'
post '/users'
patch '/users/:id'
get '/posts'
I'll give you a very simple example here:
Create a file controller.rb
get '/pages' do
#pages = Pages.all
erb :pages
end
Next create a views directory in the same folder as teh controller, and create a file named pages.html.erb
This is the corresponding view to your previously created controller action.
Here, you can type something like:
<% #pages.each do |p| %>
<%= p.title %>
<% end %>
Restart your server, visit localhost:PORT/pages and you will see a list of all your page titles.
You can check out this link for a simple sinatra tutorial - http://code.tutsplus.com/tutorials/singing-with-sinatra--net-18965
You can make this as complicated or as simple as you need. For example:
Rails makes a lot of magic happen under the hood, whereas Sinatra is more flexible at the cost of requiring you to implement some of this stuff yourself.
controller_map = {
'pages' => PagesController
}
post '/:controller/new' do
c = params[:controller]
module = controller_map[c]
module.create_new()
...
end
get '/:controller/:id/show' do
c = params[:controller]
id = params[:id]
module = controller_map[c]
module.get(id)
...
end

Padrino controller abstraction

I've been trying Padrino framework in one of my project, and there is one thing that really annoys me. I want to implement just for instance a user registration process using OmniAuth and want to break my request handler (controller's action) to separate methods, like this:
get ":provider/callback" do
#user = find_the_user_by_oauth(request)
create_user unless #user
store_user_in_session
end
def find_the_user_by_oauth(request)
#...
end
def store_user_in_session
session[:user_id] = #user.id
end
I know it would be nicer to push the logic to the model layer, but my question is, how could I break a controller logic to separated methods and share information among them (like using instance variables). In Rails I created these methods in the private scope of my controller, but here I should extend the Application class because it throws Undefined method exception for the previous code. I tried Helpers, but helpers don't know the instance variables, so you should pass the variables every time.
What is the good way to make my controller actions clean in Padrino?
To define a method inside an Padrino Controller you can use define_method instead of def.
For your example, do something like this:
Admin.controllers :dummy do
define_method :find_the_user_by_oauth do |request|
request.params["username"]
# ...
end
define_method :store_user_in_session do
session[:user_id] = #user
end
get :test do
#user = find_the_user_by_oauth(request)
create_user unless #user
store_user_in_session()
session.inspect
end
end
Padrino runs the block sent to Admin.controllers using instance_eval.
See this answer for the differences https://stackoverflow.com/a/3171649 between define_method and def
possible offtopic, but would you consider to use Espresso Framework instead.
then you'll can solve your issue as simple as:
class App < E
def index provider, action = 'callback'
#user = find_the_user_by_oauth
create_user unless #user
store_user_in_session
end
private
def find_the_user_by_oauth
# provider, action are accessed via `action_params`
# action_params[:provider]
# action_params[:action]
end
def store_user_in_session
session[:user_id] = #user.id
end
end

I want to override authenticate_user and current_user method of devise gem

I want to override authenticate_user! and current_user method of devise gem in my application Controller can you please help me with regards to that
Thanks
You may be able to monkey-patch it like:
module Devise
module Controllers
module Helpers
def authenticate_user!
#do some stuff
end
end
end
end
But I would ask what the ultimate goal is, because Devise has some customizability built into it already, and overriding these methods makes me wonder "why use Devise at all?"
On overriding how a user is authenticated:
Devise uses Warden under the hood
https://github.com/plataformatec/devise/blob/master/lib/devise/controllers/helpers.rb
So you can just add a new strategy in Warden to authenticate your users. See
https://github.com/hassox/warden/wiki/Strategies
You should not need to override current_user. What challenge are you facing ?
Do you need a different model returned ?
If you want to add code to authenticate_user!
class DuckController < ApplicationController
before_action :authenticate_duck
...
private
def authenticate_duck
#use Devise's method
authenticate_user!
#add your own stuff
unless current_user.duck.approved?
flash[:alert] = "Your duck is still pending. Please contact support for support."
redirect_to :back
end
end
end
You have to create a custom class to override the default Devise behavior:
class CustomFailure < Devise::FailureApp
def redirect_url
#return super unless [:worker, :employer, :user].include?(scope) #make it specific to a scope
new_user_session_url(:subdomain => 'secure')
end
# You need to override respond to eliminate recall
def respond
if http_auth?
http_auth
else
redirect
end
end
end
And in your config/initializers/devise.rb:
config.warden do |manager|
manager.failure_app = CustomFailure
end
But I suggest check out the Devise documentation :)
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
At application_controller.rb you can overwrite just as you want:
def authenticate_user!
super # just if want the default behavior
call_a_method_to_something if current_user
# or
call_a_method_to_something if current_user.nil?
end

How do I work with Rails 3 cookies and helpers?

I created a user and stored the id in a permanent cookie:
def save_user_id_cookie
cookies.permanent.signed[:user_id] = #user_id
end
Here is a link.
and then try to access it:
helper_method :current_user
private
def current_user
#current_user = #current_user || User.find(cookies.signed[:user_id])
end
Here is a link.
I see the cookie on my machine but when I try to load the homepage I get:
Couldn't find User without an ID
app/controllers/application_controller.rb:8:in `current_user'
The controller is here.
Believe this line
#current_user = #current_user || User.find(cookies.signed[:user_id])
should be
#current_user = #current_user || User.find(cookies[:user_id])
*side note: for little less code you can try assigning like
#current_user ||= User.find(cookies[:user_id])
In your save_user_id_cookie:
def save_user_id_cookie
cookies.permanent.signed[:user_id] = #user_id # may be #user.id?
puts 'saved cookie'
end
#user_id is nil. I think you should use #user.id instead.
Try this:
#current_user = #current_user || User.find(*cookies.signed[:user_id])
Notice the * before the cookies.
and yes, as #nash pointed out, that user_id should be actually user.id.
I didn't bother to look there for errors, as you said that you could see the cookie on your machine.

Testing #current_user method using RSpec

I've been trying to do this for a couple of days now, but I can't figure it out. I have the following code in my controller:
#some_object = #current_user.some_method
In my spec, I want to attach a should_receive hook on that method, but I can't make it work. I've tried all of these, but none of them work:
assigns[:current_user].should_receive(:some_method).at_least(:once) # expected 1, got 0
User.should_receive(:some_method).at_least(:once) # expected 1, got 0
How is the correct way of testing this? I'm running this in my spec, and login is working:
setup :activate_authlogic
...
UserSession.create(users(:rune))
Thanks!
One example comes from the Ruby on Rails Tutorial. Rather than setting and reading #current_user directly, it defines two helper methods:
def current_user=(user)
#current_user = user
end
def current_user
#current_user
end
Later, they access this method in the tests using the controller method:
def test_sign_in(user)
controller.current_user = user
end
Using this methodology, you should be able to use
controller.current_user.should_receive(:some_method).at_least(:once)
You can’t call something like in the controllers:
expect(current_user).to be_present
expect(user_signed_in?).to be_true
So to do so, you can do this :
module ControllerMacros
def current_user
user_session_info = response.request.env['rack.session']['warden.user.user.key']
if user_session_info
user_id = user_session_info[0][0]
User.find(user_id)
else
nil
end
end
def user_signed_in?
!!current_user
end
end
You can either include the ControllerMacros in the top of the controller spec or include it in the spec_helper.rb like so :
RSpec.configure do |config|
config.include ControllerMacros, type: :controller
end

Resources