Ruby on Rails: custom rendering in controller - ruby-on-rails

So basically I have this for my index page:
def index
#articles = Article.paginate(:per_page => 15, :page => params[:page])
end
I implemented soft deletion, which basically works like this: there is an additional boolean column in db that is called is_active and is true by default. Deleting is rewritten to just change that to false instead of destruction, and made a page to view soft-deleted entries.
The issue: current workaround I found for paginate is to simply add
<% if article.is_active %>
in my index.html.erb. The flaw is: when I delete something it still considered there by paginate, so instead of say 15 entries I will see 14. Even worse, on undeleting page it shows same amount of blank pages, and deleted entries are on their would-be-appropriate pages (so for example first entry may end up being on page 14 instead of 1). It is not critical flaw, but I'd like to know if I can fix it without rewriting too much.
Maybe I can change something in controller so it doesn't send any entries that have true or false in that field depending on what I want to output?

Can't you just filter by this field?
#articles = Article.where(is_active: true).paginate(per_page: 15, page: params[:page])

You can use a scope or a where clause to get only active articles:
def index
#articles = Article.where("is_active = ?", true).paginate(:per_page => 15, :page => params[:page])
end

Related

Kaminari error when collection is smaller than per(x)

I am running the Kaminari gem for my pagination.
Controller
def dashboard
#projects = Project.find_by_user_id(current_user)
if #projects.size > 10
#projects.page(params[:page]).per(10)
end
end
Dashboard view
= paginate #projects, :theme => 'twitter-bootstrap-3', :remote => true
In my case, the #projects is sometimes only 1 record or even zero records. When it is nil, I get an error on the params[:page] being nil.
So this works
def dashboard
#projects = Project.page(params[:page]).per(10)
end
This gets error undefined method 'page' for #<Project:0x007f8cac5f14b0>
def dashboard
#projects = Project.find_by_user_id(current_user).page(params[:page]).per(10)
end
I think it is because the #projects is only a couple of records which is less than the 10 specified in .per
I tried adding a #projects.count or #projects.size but I get the error undefined method 'size' for #<Project:0x007f8c996865f0>
def dashboard
#projects = Project.find_by_user_id(current_user)
if #projects.size > 10
#projects.page(params[:page]).per(10)
end
end
What the hell am I doing wrong!? haha
I am guessing I can fix this in the first instance instead of trying to fix the second or third options. Any help would be greatly appreciated.
The issue is Project.find_by_user_id(current_user) returns an Array, not an ActiveRecord::Relation
You should do something like:
current_user.projects.page(params[:page]).per(10)
If your relationships are correctly setup.
Or:
Project.where(user_id: current_user.id).page(params[:page]).per(10)

Setting default search parameter on Ransack for rails

I've been wracking my brain over this but can't get it. I feel like the answer is probably obvious.
What I'm trying to do is the following:
I have an index controller which lists a series of Jobs which I can search using Ransack. Each job has a completion date which either has a date in it or is null (unfinished). Currently, the search itself works great. I would like to make it so that the index page loads up showing only the unfinished work, but I also want it to work so that when someone does run a search, returns results for both finished and unfinished work.
Any help would be greatly appreciated. In the code below, :actual is the name of the field with the completion date. I also was looking around the web and thought that maybe something like the DEFAULT_SEARCH_PARAMETER={} that I have in the Job model might work but I couldn't seem to get it to.
Here is the code:
class Job < ActiveRecord::Base
DEFAULT_SEARCH_PARAMETER ={}
attr_accessible :items_attributes, :actual
end
def index
#search = Job.search(params[:q] || Job::DEFAULT_SEARCH_PARAMETER)
#search.build_condition
#results = #search.result
#job = #results.paginate(:per_page => 10, :page => params[:page])
end
Late to the party, but thought I'd suggest an alternate approach in case someone else comes across this.
The answer above works, but its disadvantage is that the default is not added to Ransack's search object, so - if you are using a search form - the default selection is not shown in the form.
The following approach adds the default to the search object and therefore will appear in your search form.
def index
#search = Job.search(params[:q])
#search.status_cont = 'Open' unless params[:q] #or whatever, must use Ransack's predicates here
#results = #search.result
#job = #results.paginate(:per_page => 10, :page => params[:page])
end
I think you could just apply your own filter when the search parameters don't exist:
def index
#search = Job.search(params[:q])
#results = #search.result
#results = #results.where(:your_date => nil) unless params[:q]
#job = #results.paginate(:per_page => 10, :page => params[:page])
end
Many years later I found myself with this exact problem so I thought I'd chime in with a solution I'm using. Set default search params in the controller and reverse merge them into params[:q]:
def index
default_search_params = {
status_cont: "open"
}
#search = Job.search((params[:q] || {}).reverse_merge(default_search_params))
...
end
So by default, you want the page to load with records where actual is nil. And later when the user searches you want to go back to how your search was working before.
Give this a try.
def index
#search = Job.search(params[:q] || Job::DEFAULT_SEARCH_PARAMETER)
#search.build_condition
#results = #search.result
if #results.nil?
#results=Job.find(:all, :conditions => ["actual = NULL"] )
end
#job = #results.paginate(:per_page => 10, :page => params[:page])
end

Rails Get Multiple by ID

In Rails, I have a Product model. Sometimes I need to get multiple products at the same time (but the list is completely dynamic, so it can't be done on the Rails side).
So, let's say for this call I need to get products 1, 3, 9, 24 in one call. Is this possible? If so, do I need a custom route for this and what do I put in my controller?
i.e. does something like this work? /products/1,3,9,24
I don't think you should need to change the routes at all. You should just have to parse them in your controller/model.
def show
#products = Product.find params[:id].split(',')
end
If you then send a request to http://localhost/products/1,3,9,24, #products should return 4 records.
I would consider this a request to index with a limited scope, kind of like a search, so I would do:
class ProductsController < ApplicationController
def index
#products = params[:product_ids] ? Product.find(params[:product_ids]) : Product.all
end
end
and then link to this with a url array:
<%= link_to 'Products', products_path(:product_ids => [1, 2, 3]) %>
this creates the standard non-indexed url array that looks kind of like
product_ids[]=1&product_ids[]=2 ...
Hope that helps.
Product.where(:id => params[:ids].split(','))

Moving of will_paginate to model

On my Question model I have some scopes
scope :recent, order("created_at DESC")
scope :approved, where("status = ?", "approved")
scope :answered, approved.recent.where("answers_count > ?", 0)
On my question controller I'm retrieving questions using the scopes
example 1:
#questions = Question.approved.recent
example 2:
#questions = User.find(session[:user_id]).topics.map { |t| t.questions.approved.recent }.flatten.uniq
I'm trying to put will_paginate on my model to make things easier on the controller but the 2nd example is very tricky as it is using mapping to retrieve questions according to preferences.
I've tried to add this on my model
def self.pagination(page = 1)
self.paginate(:page => page, :per_page => 5)
end
and then on my controller I have
#questions = Question.approved.recent.pagination.(params[:page])
That works fine for the 1st example but I Dont know how to implement that on the 2nd example
Any hints?
This looks like Rails 3. Be sure to use the ~> 3.0.pre2 version of the will_paginate gem.
You can use the paginate method at the end of your chain of scopes. For example, your "example 1" would be:
#questions = Question.approved.recent.paginate(:page => params[:page], :per_page => 20)
I see you created a custom method (pagination) to wrap this pattern, but it's best that you keep this syntax in original form for now, especially since you're dealing with scopes and Relation objects in Rails 3 and will_paginate doesn't have proper support for this yet (but it's coming).
In your "example 2" it seems you only need to fetch the first few recent questions from each topic and that you won't perform a full-blown pagination here (like, going to page 2 and forward). You don't have to use the paginate method here; you can simply use ActiveRecord's limit:
current_user = User.find(session[:user_id])
#questions = current_user.topics.map { |topic|
topic.questions.approved.recent.limit(5).to_a
}.flatten.uniq

will_paginate without use of activerecord

I apologize if this is a trivial question or my understanding of rails is weak.
I have 2 actions in my controller, index and refine_data.
index fetches and displays all the data from a database table.
refine_data weeds out unwanted data using regex and returns a subset of the data.
Controller looks like:
def index
Result.paginate :per_page => 5, :page => params[:page], :order => 'created_at DESC'
end
def refine_data
results = Result.all
new_results = get_subset(results)
redirect_to :action => 'index'
end
I would like to redirect the refine_data action to the same view (index) with new_results.
As new_results are not from the database table (or model), how do I go about constructing my paginate?
As I wrote in my answer to Sort by ranking algorithm using will-paginate, it is possible to use a custom find method to order the data.
It could be used similar to filter out unwanted data, since you just need to return a set of data. By modifying the name of your refine_data to something like find_refined_data you can use
Result.paginate_refined_data :per_page => 5, :page => params[:page], :order => 'created_at DESC'
in your index method. (Of course you need to return a set of records instead redirect to the index action)
BTW you could also use the paginate_by_sql method, if you are able specify your filter as a SQL query. This is probably more efficient than grabbing all records and performing a regex on them. But more complex I guess.
I was not successful in getting will_paginate to work by creating my own find method.
I was almost successful but not quite.
Here's what I've tried:
In Controller:
def refine_data
Result.paginate_refined_data :per_page => 5, :page => params[:page], :order => 'created_at DESC', :exclude =>"somestring"
end
In Model:
def find_refined_data(args)
exclude_string = args[:exclude];
new_results = do_some_work_and_exclude_records(#results,exclude_string)
end
will_paginate had trouble with me passing an additional parameter :exclude which it did not understand.
The simplest solution for me was to create my own WillPaginate::Collection object.
So here's how mine works now:
#The method name does not cause will_paginate to intercept and try to do its magic.
def new_find_refined_data(args)
exclude_string = args[:exclude];
new_results = do_some_work_and_exclude_records(#results,exclude_string)
#entries = WillPaginate::Collection.create(1, args[:per_page]) do |pager|
# inject the result array into the paginated collection:
pager.replace(new_results)
unless pager.total_entries
# the pager didn't manage to guess the total count, do it manually
pager.total_entries = new_results.length
end
end
end
Hope this will help any of the guys facing the same problem:
I'm not sure about the other answers - you may well want to move that method into your model class.
But answering your actual question.
The way to show the same view as another action is not to redirect but use:
render :action => :index
Yet another alternative might be to create a named_scope for your refined result set.
Can you formulate your "get_subset" business logic into database-style commands?
If so you can reconstruct your finder as a named_scope (with those conditions)
eg:
named_scope :blue_things, :conditions => {:colour => 'blue'}
and then just call paginate on that:
Result.blue_things.paginate :per_page => 5, :page => params[:page]

Resources