Switching language (locale) in ActiveAdmin without passing a parameter - ruby-on-rails-4

I want to be able to switch my locale in an ActiveAdmin app of mine.
So far I've followed this guide on switching-locale, which actually mentions the problem I'm having:
You will notice, however, that all links keep the default locale of your app.
So in my case, once I switch the locale, the urls stay
localhost:3000/en/admin/users instead of
localhost:3000/de/admin/users
The guide also proposes a solution:
You can override this default locale by passing the locale to all _path methods.
But this seems error-prone and quite a lot of work.
So looks like ActiveAdmin uses I18n.locale once to create all the urls and does not consider changes to I18n.locale after that.
Meaning if you this in your ApplicationController:
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
I've tried overriding
ActiveAdmin::Helpers::Routes.default_url_options
in my ApplicationController, which did not help.
Does anyone have an idea how I could resolve this issue?
Edit:
I also set these and tried different variations of the scope method.
Routes
scope '(/:locale)', locale: /en|de/, defaults: { locale: I18n.locale }
ActiveAdmin.routes(self)
end
ApplicationController
def default_url_options(options={})
{ locale: I18n.locale }.merge options
end

Ok so I've finally figured out what was going on.
I originally followed the rails guide and set my routes.rb:
# config/routes.rb
scope "/:locale" do
resources :books
end
which resulted in an error like:
No route matches {:action=>"index", :controller=>"admin/users"} missing required keys: [:locale]
this was "fixed" by setting
scope ':locale', defaults: { locale: I18n.locale } do
ActiveAdmin.routes(self)
end
as suggested in the current version of the Switching locale guide.
But this has the side-effect of all subsequent url_helpers using this locale for url generation. BTW at least one other person ran into this.
My current work-around can be found here:
lib/active_admin/helpers/routes/url_helpers.rb
def self.default_url_options
(Rails.application.config.action_mailer.default_url_options.merge({locale: ::I18n.locale})) || {}
end
Now urls are generated as expected.

First of all you need define :locale scope as optional, this can be done with this code:
scope '(:locale)' do
#your routes
end
After in ApplicationController put that code for enable default scoping:
def default_url_options(options={})
options.merge({locale: I18n.locale})
end

Related

Redirecting a request in a routing constraint

I have Sidekiq mounted in my routes file to the /sidekiq endpoint.
I use a constraints option to have it call an external class for validation as a way of preventing non-privelaged users from accessing that endpoint.
# config/routes.rb
mount Sidekiq::Web => "/sidekiq", constraints: Sidekiq::AdminConstraint.new
# lib/sidekiq/admin_constraint.rb
module Sidekiq
class AdminConstraint
def matches?(request)
return false unless request.session[:user_id]
user = User.find_by_id(request.session[:user_id])
user && Ability.new(user).can?(:manage, :sidekiq)
end
end
end
This setup works great. However, it only lets me return true / false on whether the request should go through or not. It does not let me -
Set a flash message (e.g. "You are not permitted to access that page") and
Redirect to some arbitrary page
In that sense, I'm looking for it to behave more like a controller's before_filter.
Is there a way I can modify the request object that's passed in to implement that redirect?
Thanks!
I don't have idea directly set the flash messages, But we can use in different way.
Use the following solution
In your routes.rb, add the following line in the end of the file
match "*path", :to => "application#error_404"
This basically means, any path that is not defined in your route will end up going to error_404 in application_controller. Its very important to put this at the end of your file
And in your ApplicationController, add
def error_404
redirect_to root_path
end
Thanks

correct way to use routes.rb namespace rails?

I'm trying to create a back-end area to my application, so I've created a folder named backend and backend_controller.rb inside it. I need the folder because the backend area will have multiple folders, so it's better-separated from my others.
my routes.rb looks like:
namespace :backend do
get 'index'
end
my backend_controller.rb:
class BackendController < ApplicationController
def index
end
end
But in this mode Rails will search for my backend_controller.rb inside the controllers folder, not in controller>backend. I've tried many variations, and I get routing errors.
So what is correct way to do that? To set the path /backend to index action instead of /backend/index?
Thanks
What i've done:
based on all answers, principally the one from Cyril DD
I've created the backend_controller.rb on the app/controller folder and in the sub-folder app/controller/backend i created the static_pages_controller.rb and all files looks like this:
app/controllers/backend_controller.rb:
class BackendController < ApplicationController
end
app/controller/backend/static_pages_contter.rb:
class Backend::StaticPagesController < BackendController
def dashboard
end
end
routes.rb:
namespace :backend do
resource :static_pages, path: '', only: [] do
root to:'static_pages#dashboard'
end
this works fine, but cause i'm newbie on rails i must ask. This is a good or a conventional way to do that? to administrate the permissions which user can see on the backend i use the backend_controller.rb right? and at last wy i must use resource: instead just get ''
Answering your question
Alright, namespace :something is a shorthand for scope 'something', module: 'something', as: 'something'
Now your declaration is very ambiguous, because you don't specify a controller. Typical declarations look like (assume you have a controller controllers/backend/some_resources_controller.rb and you want to generate default routes)
namespace :backend do
resources :some_resources
end
Now what you did
namespace :backend
get 'index'
end
is really ambiguous and I'm not surprised it doesn't do what you want. Basically you just tell rails to "look inside subfolder 'backend' and define the route 'index'". oO ? Which file/controller are we even talking about ?
What is your backend_controller.rb supposed to do ? Is it some kind of Control Panel ? Dashboard ? If so you're probably gonna have a lot of non-CRUD actions, but anyways you should go for the following syntax
namespace :backend
# Below line of code will auto-generate the `index` for /backend/backend_controller
resource :backend, only: [:index], path: '' do # we need " path: '' " otherwise we'll have https://xxx/backend/backend/dashboard
# If you have non-CRUD actions, put them here !
get 'dashboard' # https://xxx/backend/dashboard
...
end
# However, this will create URLs like "https://xxx/backend/dashboard", etc.
# If you want to redirect https://xxx/backend/ to your backend_controller#index, use root
root to: 'backend#index' # https://xxx/backend/
end
Last thing as mentionned by other guys, when you namespace a file like your Backend_controller inside /backend/ subfolder, you must rename the class like (/controllers/backend/backend_controller)
class Backend::BackendController < ApplicationController
Remark : if you only have like one or two controller actions, instead of using the resource method, you can declare singular resources
namespace :backend do
root to: 'backend#dashboard'
get 'dashboard', to: 'backend#dashboard' # singular resource
end
An Example of what you may actually really want to do...
I'm not sure you are clear yourself about what you want to do. As an example, here is my architecture
Files
/controllers/application_controller.rb
/controllers/backend_controller.rb
/controllers/backend/static_pages_controller.rb
/controllers/backend/***.rb
The class /controllers/backend_controller.rb will not serve any action, but will override ApplicationController to tune it for backend access (but maybe you don't need to do so)
class BackendController < ApplicationController
# Do you need to change user_access method ? Or any other backend-wide config ?
# If so put this config here, otherwise leave empty
end
Now for every file that goes in the /backend/ subfolder, I inherit the backend_controller
class Backend::StaticPagesController < BackendController
def index
end
# Note : if your index is some kind of dashboard, instead I would declare
def dashboard
end
end
class Backend::SomeResourcesController < BackendController
...
end
Routes
namespace :backend do
root to 'static_pages#index' # https://xxxx/backend/
resource :static_pages, only: [:index], path: '' # https://xxxx/backend/index
resources :some_resources
end
If you choose the dashboard solution in your controller, write instead :
namespace :backend do
root to: static_pages#dashboard # https://xxxx/backend/
resource :static_pages, path: '', only: [] do
get 'dashboard' # https://xxxx/backend/dashboard
end
resources :some_resources
end
Then it's simply
# routes.rb
Rails.application.routes.draw do
namespace :backend, shallow: true do
resource :backend, path:''
end
end
Then in your app/controllers/backend/backend_controller.rb, it'd look like this.
class Backend::BackendController < ApplicationController
def index
end
end
When I use rake routes it shows
Prefix Verb URI Pattern Controller#Action
backend_backends GET /backend(.:format) backend/backends#index
Hope this helps.

Globalize: How to show in the view the current locale information

I am currently having a bit of a problem with Globalize gem.
I explain the current situation:
I have a Model called Question. After creating it, without any data stored, I added the following lines to the model:
class Question < ActiveRecord::Base
translates :wording, :answer1, :answer2, :answer3, :answer4
end
Then, I created a migration to create the translations table
class CreateTranslationsTable < ActiveRecord::Migration
def up
Question.create_translation_table! :wording => :string, :answer1 => :string, :answer2 => :string, :answer3 => :string, :answer4 => :string
def end
def down
Question.drop_translation_table!
def end
My default locale is :en. After that I added some data.
If I go execute rails c and put the command Question.first.wording everything works fine.
Although when I execute in 'rails c' I18n.locale = :es and then I do Question.first.wording still displays the english text I put at the beginning.
I tried one thing which it seemed to help me is that I dropped all the translated columns (like specified in Globalize documentation after you migrated data. In my case I didn't have any data to migrate at the beginning though). After that I made a rollback (which got back the columns I deleted form the Question model), then executing Question.first.wording with I18n.locale = :es got it working. Which means that Question.first.wording returns nil.
After that, I implemented the 'Locale from Url Params' as specified in the Ruby on Rails guide
Which means the first URL param si the ':locale' param.
Now the current problem: The view still displays the information in English when it should display it in Spanish, since the URL I entered was http://localhost.com/es/questions/.
How can I make it to display in the view the Spanish information?
My mistake. I interpreted from the documentation that the the chunck of code (in application_controller.rb) that works for setting the url:
def default_url_options(options={})
{ locale: params[:locale] }
end
would actually set the 'I18n.locale' variable. What I did is the next to get around this (in application_controller.rb):
before_action :change_to_current_locale
def change_to_current_locale
I18n.locale = params[:locale]
end
That made it work.

Rails Routes not conforming to expected controller action

I've spent several hours trying to understand why my Rails 4 app doesn't seem to want to route to the expected controller action.
In summary: every single action that I attempt in the browser URL seems to go to the index view, even though my routes appear to be correct. I've attempted to restart the server, etc hoping that might fix it, but right now I'm completely lost.
For example, if I try to access the URL at localhost:3000/leads#new I get the following error message:
Missing template leads/index, application/index with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in: * "/Users/me/Sites/azimuth/app/views"
If I add a template for index.html.erb to the app/views/leads folder, then I don't get the error message - however every single path goes to that same index view - leads#show, leads#edit, etc - all of them.
Here is the routes.rb:
Azimuth::Application.routes.draw do
# get 'leads', to: 'leads#new'
resources :applicants
resources :contacts
resources :leads
PagesController.action_methods.each do |action|
get "/#{action}", to: "pages##{action}", as: "#{action}_page"
end
root "pages#home"
end
note that the commented line - get 'leads', to: 'leads#new' - seems to be the only way to properly get the routing to work. Using resources :leads (which I understand is best practice?) is giving me fits.
Here's the leads_controller.rb:
class LeadsController < ApplicationController
def new
#lead = Lead.new
end
def create
#lead = Lead.new(lead_params)
if #lead.save
flash[:success] = "Thank you for reaching out! We'll be in touch soon."
redirect_to 'home'
else
render 'new'
end
end
def index
#lead = Lead.all
end
private
def lead_params
params.require(:lead).permit(:first_name, :last_name, :subject, :message)
end
end
Rake routes - appears that things should work fine. (Note this is just showing the routes relevant to the Leads object).
Prefix Verb URI Pattern Controller#Action
leads GET /leads(.:format) leads#index
POST /leads(.:format) leads#create
new_lead GET /leads/new(.:format) leads#new
edit_lead GET /leads/:id/edit(.:format) leads#edit
lead GET /leads/:id(.:format) leads#show
PATCH /leads/:id(.:format) leads#update
PUT /leads/:id(.:format) leads#update
DELETE /leads/:id(.:format) leads#destroy
I'm very confused, can't seem to track down what's going on, and would appreciate any help!
Correct me if you are wrong, but I think you are trying to access the wrong URL. You said you were visiting localhost:3000/leads#new in your browser. The correct URL for that route would be localhost:3000/leads/new
When are you are defining routes in your config/routes.rb file, the #'s are used to let rails know that you are specifying that a method of one of your controllers should respond to this URL. The actual URL's do not contain #'s (typically speaking).

Devise+Omniauth, routes versioning

I have a model Candidate which is devise omniauthable (linkedin).
So far, my routes.rb looked like this :
namespace :v1 do
devise_for :candidates, only: :omniauth_callbacks
...
end
Everything worked well till I had to add a new version :
namespace :v2 do
devise_for :candidates, only: :omniauth_callbacks
...
end
namespace :v1 do
devise_for :candidates, only: :omniauth_callbacks
...
end
With the current configuration, I get this error :
`set_omniauth_path_prefix!': Wrong OmniAuth configuration. If you are getting this exception, it means that either: (RuntimeError)
1) You are manually setting OmniAuth.config.path_prefix and it doesn't match the Devise one
2) You are setting :omniauthable in more than one model
3) You changed your Devise routes/OmniAuth setting and haven't restarted your server
It's kind of annoying since I want to be able to authenticate the candidate on both versions.
What can I do ?
Alright, let's recap a little bit here, Devise doesn't allow you to call the devise_for method inside a scope or a namespace route defined in the config/routes.rb file, right?
My namespace'd route looks like this:
namespace :api, constraints: { format: :json } do
devise_for :users, skip: [ :registrations, :passwords, :confirmations ]
resources :profiles, only: :show
end
And it works!
What did I do to make it work? the answer lies in the config/initializers/devise.rb file.
Check out near the bottom of the File it says...
# When using omniauth, Devise cannot automatically set Omniauth path,
# so you need to do it manually. For the users scope, it would be:
The next commented line shows you an example, uncomment that line and modify it according to your needs, for my case(ie. for the namespaced route I have above) I have:
config.omniauth_path_prefix = "/api/users/auth"
And that's it! .... I did that and it all started to work perfectly!
Hope it helps!