My route for pages in routes.rb
get ":slug", to: 'site#pages'
my actions in site_controller.rb
def pages
render #page.page_template
end
def about
end
def contact
end
def content
end
def local_news
end
def global_news
#newscasts = Newscast.published.paginate(page: params[:page], per_page: 5)
end
and it's my error :)
Not see global_news action my #newscasts parameter
You need to define #newscasts inside pages method
#newscasts = Newscast.published.paginate(page: params[:page], per_page: 5)
Or you can write this in your controller above your methods.
before_action :global_news, only: [:pages]
Before action will run your global_news methods before every action defined inside only: in your case you can write (:pages) you can mention as many methods you want. If you remove only then global_news will run before every method.
This cast an error because you are just rendering the global_news. With render you are not executing the controller action. So #newscast is never set.
You can either use a before filter as in the other answer or call the method manually, because I think you are doing something dynamically here, right?
for example
def pages
global_news
render #page.page_template
end
Related
I have a rails app where many of the models are editable using best_in_place, so I have a lot of controllers that look partially like this:
before_action :find_objects, except: [:new, :create]
def update
#object.update_attributes(object_params)
respond_to do |format|
format.json { respond_with_bip #object}
end
end
private
def object_params
params.require(:object).permit(:foo, :bar)
end
def find_objects
#object = Object.find(params[:id])
end
How do I move this particular repeated piece into a controller concern, given that the object being updated is going to come in with a particular name in the params hash, and object_params and find_objects should call their proper versions based on the model name? Is there some elegant meta-magic that'll sort this all out?
I think this is a case where your code could be "too DRY". You can certainly accomplish this using meta-magic, but it could make your code confusing in the long run.
If you want to do the meta-magic, one trick is to use params[:controller] to get the name of the model. For example, if you have a PostsController, then:
params[:controller] # => "posts"
params[:controller].classify # => "Post"
Taking this a step further, you could write a generic find_object like this:
def find_object
model_class = params[:controller].classify.constantize
model_instance = model_class.find(params[:id])
instance_variable_set("##{model_class.name.underscore}", model_instance)
end
But as I said at the beginning, I'm not sure I would recommend this amount of abstraction just for the sake of DRY-ing your controller code.
My show action:
def show
# Multiple keywords
if current_user.admin?
#integration = Integration.find(params[:id])
else
#integration = current_user.integrations.find(params[:id])
end
#q = #integration.profiles.search(search_params)
#profiles = #q.result.where(found: true).select("profiles.*").group("profiles.id, profiles.email").includes(:integration_profiles).order("CAST( translate(meta_data -> '#{params[:sort_by]}', ',', '') AS INT) DESC NULLS LAST").page(params[:page]).per_page(20)
#profiles = #profiles.limit(params[:limit]) if params[:limit]
end
There can be many different filters taking place in here whether with Ransacker, with the params[:limit] or others. At the end I have a subset of profiles.
Now I want to tag all these profiles that are a result of the search query.
Profiles model:
def self.tagging_profiles
#Some code
end
I'd like to create an action within the same controller as the show that will execute the self.tagging_profiles function on the #profiles from the show action given those profiles have been filtered down.
def tagging
#profiles.tagging_profiles
end
I want the user to be able to make a search query, have profiles in the view then if satisfied tag all of them, so there would be a need of a form
UPDATE:
This is how I got around it, don't know how clean it is but here:
def show
# Multiple keywords
if current_user.admin?
#integration = Integration.find(params[:id])
else
#integration = current_user.integrations.find(params[:id])
end
#q = #integration.profiles.search(search_params)
#profiles = #q.result.where(found: true).select("profiles.*").group("profiles.id, profiles.email").includes(:integration_profiles).order("CAST( translate(meta_data -> '#{params[:sort_by]}', ',', '') AS INT) DESC NULLS LAST").page(params[:page]).per_page(20)
#profiles = #profiles.limit(params[:limit]) if params[:limit]
tag_profiles(params[:tag_names]) if params[:tag_names]
end
private
def tag_profiles(names)
#profiles.tagging_profiles
end
In my view, I created a form calling to self:
<%= form_tag(params.merge( :controller => "integrations", :action => "show" ), method: :get) do %>
<%= text_field_tag :tag_names %>
<%= submit_tag "Search", class: "btn btn-default"%>
<% end %>
Is this the best way to do it?
Rails public controller actions correspond always to a http request. But here there is just no need for 2 http requests. A simple solution would be just creating to private controllers methods filter_profiles(params) and tag_profiles(profiles) and just call them sequentially.
You can also extract this problem entirely to a ServiceObject, like this:
class ProfileTagger
attr_reader :search_params
def initialize(search_params)
#search_params = search_params
end
def perform
search
tag
end
def tag
#tag found profiles
end
def search
#profiles = #do the search
end
end
As processing 30,000 records is a time consuming operation, it would make sence to perform it outside of the rails request in background. This structure will allow you to delegate this operation to a sidekiq or delayed_job worker with ease
Instance Variables
If you want to "share" variable data between controller actions, you'll want to look at the role #instance variables play.
An instance of a class means that when you send a request, you'll have access to the #instance variable as long as you're within that instance of the class, I.E:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
before_action :create_your_var
def your_controller
puts #var
end
private
def create_your_var
#var = "Hello World"
end
end
This means if you wish to use the data within your controller, I would just set #instance variables, which you will then be able to access with as many different actions as you wish
--
Instance Methods
The difference will be through how you call those actions -
#app/controllers/your_controller.rb
Class YourController < ApplicationController
def action
#-> your request resolves here
method #-> calls the relevant instance method
end
private
def method
#-> this can be called within the instance of the class
end
end
So I am trying to change the layout of a view based on url params.
So far, I figured out I have to set the layout in the controller. In my controller under the show action I have:
if params['iframe'] == 'true'
render :layout => 'vendored'
end
The layout 'vendored' exists in views/layouts. I am getting the dreaded rendering multiple times. Here is the rest of the show action in my controller:
def show
#event = Event.find(params[:id])
#user = current_user
#approved_employers = current_user.get_employers_approving_event(#event) if user_signed_in?
respond_with(#event)
The problem is that I don't see another render. I don't see another one in the entire controller. Of course, there is a render somewhere because it is rendering my default application layout, is that causing the problem? I read in the rails docs that I can add
and return
to the end and that should fix the problem, but not sure where to put that since the two renders are not next to each other. I also don't see any other redirect_to's either. Where should I be looking for this other render? Is that the problem?
Alternatively, I think this is easier to understand:
class YourController < ApplicationController
layout :iframe_layout
private
def iframe_layout
params['iframe'] ? "vendored" : "application"
end
end
See this answer. For your case:
before_filter :set_layout, :only => [:show]
private
def set_layout
self.class.layout ( params['iframe'] == 'true' ? 'vendored' : 'application')
end
My controller:
class SchoolController < ApplicationController
def index
...
end
def edit
#school=School.find_by_id params[:id]
end
def check_teachers
#teachers = #school.teachers
#How to show teachers' names and titles in a lightbox by javascript ?
end
end
As you see above, I have a check_teachers method, inside which I got a list of teachers objects. Each Teacher object has name and title attributes.
A button click on the view will trigger the check_teachers method get called:
I would like to show all teachers name and title in a lightbox. I think I would need javascript to implement this. But I don't know how can I pass all the teachers' data from Rails to javascript and show the data in a js implemented lightbox...
Anyone can provide any help on this?
you can do it with ajax. Simpliest way is to use FancyBox(jquery plugin, http://fancybox.net/home )
your button code should looks like
<a id="ajax_button" href="<%= url_for :controller => :school, :action =>:check_teachers, :id=>#school.id %>">Check teachers</a>
then add this javascript
$(document).bind('load', function() { $("#various3").fancybox({ajax:{type : "GET" } })
and your method controller action should looks like
def check_teachers
#school = School.find(params[:id])
#teachers = #school.teachers
end
but it's better to move #school = School.find(params[:id]) to before_filter
Let's say I have a User model, and an Invoice model with a belongs_to :user association.
Now I'm creating a new action for my InvoicesController, and the view that will be rendered. The view will have a select-element for selecting the user that this invoice will belong to.
So I need to fetch those users somewhere; my instinct is to leave this kind of thing out of the view. I end up with this:
def new
#users = User.all
end
The form submit action is create. When the creation fails for some reason, I re-render the new action's view.
def create
invoice = Invoice.new params[:invoice]
if invoice.save
flash[:notice] = 'Invoice created!'
redirect_to :action => 'show', :id => invoice.id
else
#users = User.all
render :action => 'new'
end
end
But as you can see, in order the re-render the new action, I have to fetch the users again.
This is just an example, but consider that I have some forms with several select-elements filled from the database, or similar constructs. That turns into an awful lot of repetition.
So how should I structure my controller in this situation?
Should I simply use User.all from my view?
Should I call new from within create to do the fetching for me?
Or something else?
For this I'd use a before_filter. For example you'd do something like:
before_filter :fetch_all_users, :only => [:new, :create]
protected
def fetch_all_users
#users = User.all
end
For 90% of my controllers I use the inherited resources plugin. It cuts down the amount of controller code you need to write for CRUD controllers, which also means you can cut down on the amount of tests you need to write.
For me:
What's the rails way to load other models collections for new, edit update and create actions?
It's not a good approach for my situation. Where after ".save", I send redirect_to to an another action, if I use before_filter and ".save" returns true, the fetch_all_users is called unnecessary