Our websites should allow to show different contents related to the given url .. something like a multisite in wordpress where we have one installation and serve the content according to the url.
as it is necessary to have the routes in the correct language I want to use a "dynamic route" approach to serve the right content. My problem is now that I dont find a way how to serve the proper routes in routes.rb if they are dynamic.
How can I "access" or "pass" the request object into any method inside the routes.rb file
f.e. like this
routes.rb
Frontend::Application.routes.draw do
DynamicRouter.load request
end
app/models/dynamic_router.rb
class DynamicRouter
def self.load request
current_site = Site.find_by_host(request.host)
Frontend::Application.routes.draw do
current_site.routes do |route|
get "#{route.match}", to: "#{route.to}"
end
end
end
end
this doesnt work because request is undefined in routes.rb
To answer your question: How can I "access" or "pass" the request object into any method inside the routes.rb file Obtain it as ENV object from rack middleware.See code below
# lib/dynamicrouterrequest.rb
require 'rack'
class DynamicRouterRequest
def initialize(app)
#app = app
end
def call(env)
request=Rack::Request.new(env)
ENV["OBJ_REQUEST"]=request.inspect.to_s
#app.call(env)
end
end
Grab it again in routes
# routes.rb
Frontend::Application.routes.draw do
request=ENV["OBJ_REQUEST"]
DynamicRouter.load request
end
A possible soluction is to create the default rules on routes.rb and add a rack middleware that can transform a path according to the domain
# routes.rb
get '/category/:id', :to => 'categories#show'
In the middleware you can transform a path like 'categoria/:id' to '/category/:id' if the domain matches '.es', before the application hits the router layer.
More on rack middleware: http://guides.rubyonrails.org/rails_on_rack.html
Related
I am following this tutorial here on setting up a user login https://learn.co/lessons/sinatra-user-auth
It says that in order to save a session[:id] in my route I would do something like this:
post '/login' do
#user = User.get_id(params['email'],params['password'])
session[:id] = #user # equals a numeric value of the matching user
redirect '/home'
end
however, when i get to my /home route I am no longer able to access the session[:id] that was set in my /login route, it returns nil
# this doesnt return the session id
# consequently I cant lookup my user records
get '/home' do
#user = User.object(session[:id]) # looks up user object with numeric value
erb :home
end
I have tried enabling sessions in my config controller, that didnt work either
module Controllers
class ApplicationController < Sinatra::Base
# . . . .
configure :production, :development do
enable :sessions, :logging
end
end
end
how do I make my app persist the session[:id] between routes?
Have you tried enable :sessions after you require your sinatra gem in your sinatra app? Not in your controller
I have a Sinatra API file that has following code-
require 'json'
require_relative 'api_logger'
include ApiLogger
get /myapi/:id
request_params = request.env
write_log('log message')
end
Then I have a module containing the methods 'write_log'-
module ApiLogger
def write_log(message)
file.write(request['user']+message)
end
But request['user'] is coming out blank.
So the question is how to access the request variable from Sinatra API file in ApiLogger module? Also, I'm creating service class objects from API class and pass them request object at initialization. Can the module 'ApiLogger' access that 'request' instance variable from service class if the service classes just include 'ApiLogger'?
You could pass it as an additional argument.
Something like:
require 'json'
require_relative '../../lib/helpers/api_logger'
include ApiLogger
get /myapi/:id
request_params = request.env
write_json_log('log message', request)
end
and
def write_json_log(message, request)
file.write(request['auth_subject']+message)
end
I did not want to pass request object to each method. So I made 'request_params' a global variable in all classes that need to log and added this line in 'ApiLogger' to fetch the value of request object-
request = instance_variable_get '#request_params'
You were almost there, all you needed was include your module in the helpers in order to have a direct access to the request object. Here's a slightly modified version of your code that runs as a standalone program:
require 'sinatra'
module ApiLogger
def write_log(message)
$stdout.write(request.env['sinatra.route'] + message)
end
end
helpers do
include ApiLogger
end
get '/test' do
write_log('log message')
'ok'
end
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
My data of a datatable:
def data
theusers.map do |usermap|
[
h(usersmap.spriden_last_name),
h(usermap.spriden_first_name),
h(usermap.spriden_id),
link_to(usermap.gobtpac_username, detail_path(usermap.spriden_id))
]
end
end
the above code resides in app\datatables\helpdesk_datatable.rb
The above works mostly I know it is getting the data, the error I get is with the detail_path
Error on "undefined method" for detail_path... This means it is not building the router dynamically right, correct?
Or I am passing in the wrong thing I tried to pass in usermap.spriden.id and just banner user, same issue. I am really not sure how routes work apparently. I have a details_controller.rb in controllers that has a show method in it and i have the views/details/show.html.erb which will show the data that was passed into the route, at least I thought. But is it just an ID or an object? so if it just an id i have to look it up again in
the show method right? How do routes like this look? I am using devise and cancan too here is my routes file:
NmsuMyaccount::Application.routes.draw do
authenticated :user do
root :to => 'home#index'
match 'home', :to => 'home#index', :via => :get
end
#get 'show-details' => "details#show", as: 'show_details'
resources :details
devise_for :users
resources :users
# In order for an unauthorized user access this controller#action, this needs to be in a scope, but I don't know why.
devise_scope :user do
match 'home', :to => 'home#index', :via => :get
end
end
Also hitting the end point localhost:3000 is an error, I have to goto /home, although devise does work just fine. So thought I was close but for the life of me cannot get the detail_path to work, and I thought it was a plural issue so tried details, and just detail no path etc. No dice.
I don't believe that you have access to the route helpers that Rails provides inside your custom class. So you have to manually include the module inside your class. Something like:
link_to(usermap.gobtpac_username, Rails.application.routes.url_helpers.detail_path(usermap.spriden_id))
Or:
include Rails.application.routes.url_helpers
# Use it like you are using.
See here for more information about the subject:
Can Rails Routing Helpers (i.e. mymodel_path(model)) be Used in Models?
I looked at the Sinatra docs and they only seem to reference HTTP authentication. I'm looking for a really simple way to control access to routes based on a user being authorised/authenticated via an LDAP server.
I've already built a class that does the LDAP bit and returns an LDAP object if the user has successfully authenticated and nil if they haven't:
>>DirectoryUser.authenticate('user', 'password')
#<DirectoryUser:0x007ffb589a2328>
I can use this to determine if they've successfully authenticated or not.
As a next step I want to splice this into a simple Sinatra app that provides a form to collect the LDAP user and password:
require 'directoryUser'
require 'sinatra'
enable :sessions
get '/form' do
username = params[:username]
password = params[:password]
haml :form
end
Then I want to only allow routes if the 'DirectoryUser' object exists:
get '/protected' do # Only if DirectoryUser object exists
"This route is protected"
end
get '/unprotected' do
"This route is unprotected"
end
I've spent hours trying to find an answer to this but so far and can't seem to find anything that works for me.
I'd probably go with something like this:
require 'directoryUser'
require 'sinatra'
enable :sessions
helpers do
def authorize!
redirect(to('/login')) unless session[:user_id]
end
end
get '/login' do
haml :login # with the login form
end
post '/login' do
user = DirectoryUser.authenticate(params[:username], params[:password])
if user
session[:user_id] = user.id
# Or: session[:logged_in] = true, depending on your needs.
redirect to('/protected')
else
redirect to('/login')
end
end
get '/protected' do
authorize!
'This route is protected'
end
get '/unprotected' do
'This route is unprotected'
end