In one of my controllers I want to change the layout given some condition, and otherwise keep the default layout used by the parent ApplicationController (was "application" initially, but I'm trying some others now). Tried accessing the "layout" using alias_method but it doesn't seem to work. My code:
class SomeController < ApplicationController
alias_method :parent_layout, :layout
layout :some_layout
def some_layout
if some_condition
"new_layout"
else
:parent_layout
end
end
end
This gives an error:
ActionController::RoutingError (undefined method `layout' for class `SomeController'):
app/controllers/some_controller.rb:6:in `alias_method'
app/controllers/some_controller.rb:6:in `<class:SomeController>'
app/controllers/some_controller.rb:3:in `<top (required)>'
It looks like you have a bunch of options. Check out the docs here (search for "finding layouts")
http://guides.rubyonrails.org/layouts_and_rendering.html
Some possibilities, depending on just how complex you need it to be:
# Proc-based
class ProductsController < ApplicationController
layout Proc.new { |controller| controller.request.xhr? ? "popup" : "application" }
end
# Route based, :except and :only
class ProductsController < ApplicationController
layout "product", except: [:index, :rss]
end
# Method-based
class OldArticlesController < SpecialArticlesController
layout false
def show
#article = Article.find(params[:id])
end
def index
#old_articles = Article.older
render layout: "old"
end
# ...
end
I'm not sure how your code is structured but it looks like the first could work for you:
class SomeController < ApplicationController
layout Proc.new { |controller| controller.some_condition? ? "new_layout" : "application" }
end
Related
I need to reference user#role to define an association in a module. I've tried with a block as shown below, but that doesn't work. How does Rails implement behavior like this?
class User < ActiveRecord::Base
include Profile
has_profile { |user| { class_name: "#{user.role}::Profile" }}
end
module Profile
extend ActiveSupport::Concern
module ClassMethods
def has_profile(&block)
role = ### How to access #role ? ###
class_eval do
has_one :profile, class_name: "#{role}::Profile"
end
...
You might need to do something like this. I didn't tested and I'm just supposing you can do this kind of stuff
class User < ActiveRecord::Base
include Profile
has_profile { |user| user.role }
end
module Profile
extend ActiveSupport::Concern
included do
after_initialize :_init_profile
end
def _init_profile
role = #_role_block.call(self)
# Here we do class eval on singleton so we dont change base class
# I'm not sure if this works as it is but should be close enought
class << self; self; end.class_eval do
has_one :profile, class_name: "#{role}::Profile"
end
end
module ClassMethods
def has_profile(&block)
#_role_block = block
...
This works:
module Models
module Profile
extend ActiveSupport::Concern
included do
after_initialize :_init_profile
end
module ClassMethods
def has_profile(&block)
#profile_association_block = block
end
end
def _init_profile
block = self.class.instance_variable_get :#profile_association_block
self.class.has_one :profile, block.call(self)
end
I am trying to render a .txt.erb file which uses a presenter to display values. The following code is inside ConfigurationWorker which is perfomed by resque:
#configuration = Configuration.first
#view = Rails.root.join 'lib', 'templates', 'config.txt.erb'
ERB.new(File.read(#view)).result(binding)
The config.txt.erb looks like this (shortened for simplicity):
<% present #configuration do |presenter| %>
Name <%= presenter.name %>
<% end %>
Whereas present is provided by ApplicationHelper and ConfigurationPresenter.
module ApplicationHelper
def present(object, klass = nil)
klass ||= "#{object.class}Presenter".constantize
presenter = klass.new(object, self)
yield presenter if block_given?
return presenter
end
end
class ConfigurationPresenter < ApplicationPresenter
presents :configuration
delegate :name, :configuration
# Presenter methods omitted
end
class ApplicationPresenter
def initialize(object, template)
#object = object
#template = template
end
def self.presents(name)
define_method(name) do
#object
end
end
def method_missing(*args, &block)
#template.send(*args, &block)
end
end
However, this results in NoMethodError: undefined method present for ConfigurationWorker:Class.
I also tried other approaches, like
#configuration = Configuration.first
renderer = ApplicationController.view_context_class.new
renderer.render :file => Rails.root.join('lib', 'templates', 'config.txt.erb')
which results in ActionView::Template::Error: uninitialized constant NilClassPresenter.
What is the proper way to make both the helper and the presenter available and pass in the variables?
I have a engine inside the lib folder named Support. In that folder, I have a Ticket controller.
I have created an ability class in the main app and I'm trying to manage all the models for the admin role. When I call the Tickets controller, it throws the error:
NameError in Support::TicketsController#index uninitialized constant Ticket
The app/model/ability.rb file is:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.role? == :admin
can :manage , :all
end
end
end
The lib/support/tickets_controller.rb file is:
module Support
class TicketsController < Support::ApplicationController
load_and_authorize_resource
respond_to :html, :xml, :json
def index
end
end
end
If the model class is namespaced differently than the controller, you will need to specify the :class option.
module Support
class TicketsController < ApplicationController
load_and_authorize_resource :class => Support::Ticket
end
end
I would like to set a default value based on the User creating it and I wonder how to do that:
class Item < ActiveRecord::Base
belongs_to :invoice
after_initialize :default_values
...
private
def default_values
self.tax_rate = current_user.tax_rate || 0
end
end
The problem is that I can't use the current_user inside the model.
This is what I've got in my controller:
def create
#invoice = Invoice.new(params[:invoice])
3.times { #invoice.items.build }
...
end
Can anybody help?
You can pass attributes to the build method, so you can remove the after_initialize ed edit the controller:
def create
#invoice = Invoice.new(params[:invoice])
3.times { #invoice.items.build( :tax_rate => current_user.tax_rate }
...
end
I have following controller:
class CarsController < ApplicationController
autocomplete :user, :name
before_filter :require_user, :except => [:my_action]
def index
end
...
def my_action
end
end
I want to allow to see all actions in this controller only for log in users - this works me fine. But the action my_action I would like to have accesible for everyone - also for a people who are not log in.
I tried to set :before_filter with the :except parameter, also with the :only parameter, but nothing works me... The app always want for me to be log in... what I am doing still wrong?
EDIT: require_user from application_controller.rb:
def require_no_user
logger.debug "ApplicationController::require_no_user"
if current_user
#store_location
flash[:warning] = "You must be logged out to access this page"
redirect_to account_url
return false
end
end
Use
skip_before_filter :require_user, :only => [:my_action]