How to stop will_paginate from getting every post from the database - ruby-on-rails-3

I am just playing around with Ruby on Rails 3.0 with a simple message board and found several issues with will_paginate.
The most pressing is that each time a new page is displayed a database query of every single post in the topic is performed.
As you can imagine, if you have a topic with 10,000+ posts this is very slow.
Is there a way to stop this odd behavior?
Show controller:
#posts=#topic.posts
#posts = Post.paginate #posts, :page => params[:page],:order => "post_number"
Model
cattr_reader :per_page
##per_page = 20
view
<%= will_paginate #posts %>

In your controller try:
#posts = Post.paginate_by_topic_id #topic.id, :page => params[:page],:order => "post_number"
Look at the example in the will_paginate docs

Upgrade will_paginate to version 3.0.0. Then:
class Post
self.per_page = 20
end
#topic.posts.page(params[:page]).order("post_number")

Related

Paginate an include variable

I'm building an app which have group > posts > comments
To reduce the number of SQL requests, i'm using the includes method
# group controller
def show
#posts = #group.posts.includes(:comments)
end
Now, I would like to paginate comments. But I don't know how to use the function .paginate from the gem will_paginate
Do you have a tips for that ?
Issue with pagination inside controller
Since you want paginated comments associated with individual post, it's complicate to achieve in controller as you need to create N paginated comment object (say you have N posts).
What you can do
1.load posts as usual with included comments to reduce excess queries. But don't run paginate here
2.achieve pagination inside view only
<%= #posts.each do | post | %>
<% comments = post.comments.paginate(page: params[:page]) %>
..
...
...
<%= will_paginate comments%>
<% end %>

No route matches {:controller=>"results", :action=>"show"}

Im upgrading a rails 2 app to rails 3 app and am having a bunch of routing issues. Heres the current:
So in the page its trying to load (results/_form.html.erb) I have a form with the following syntax:
<%= form_tag(:controller => "results",:action => "show") do %>
And i do have an action in my results_controller.rb called show. Yet i keep getting the no route matches error. Is this rails 2 syntax and not 3? Is there something I need to do in my routes.rb file? I think there is because that was a a major change between rails 2 and 3, im just not sure what. Any suggestions?
show action ideally should expect id to be passed in the params:
<%= form_tag(:controller => "results",:action => "show", :id => #user.id) do %>

Beginner struggling with update_attribute command

I am in the process of trying to use the update_attribute command, but struggling to get it working (at all) and hoped someone could point me in the right direction?
I have previously posted a question about this issue, it was very useful in terms of giving a feel for the mechanics of what is going on, but unfortunately it didn't actually get it working.
I have a database of items (Items), which among other things contains ':item_name', ':click_count' and ':external_url'.
Currently I have a view (Showselecteditems) in which there is a list of all the items, when a user clicks on an item name, they are directed to the appropriate external url. This works using the code:
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url %>
I would now like to add the ability to count the number of times a particular item name has been clicked on (i.e. in total for all users, not individual users) and therefore the number of times each external url has been visited in order to work out which is most popular.
Reading around, I believe i need to modify the code above to something of the form:
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
And need to define this function somewhere - it seems to only be found if located in 'application_helper'?
def clickcountplusone
clickeditem = Items.find(params[:identifier])
clickeditem.update_attribute(:click_count, clickeditem.click_count + 1)
rescue ActiveRecord::RecordNotFound # to avoid error if no identifier value
end
Needless to say, I cannot get this to work... My question is therefore, how can I set things up correctly so that when the link is clicked on the count is incremented? The other common problem people seem to report is that the number will be incremented each time the page is refreshed, which I would like to avod if possible.
Previously people have suggested adding to the 'show' section of the 'Items' controller, however, i don't know how this would work as the links are being clicked on the Showselecteditems view page, not the database itself where you get the show, edit, destroy commands. Any advice greatly appreciated.
This
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
will not point user to the some_controller#clickcountplusone, because you already specified an external link.
The easiest way to do this job is to modify your link_to like:
<%= link_to selecteditem.item_name.to_s, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
And then to modify your actions source:
def clickcountplusone
clickeditem = Items.find(params[:identifier])
redirect_to clickeditem.external_url if clickeditem.update_attribute(:click_count, clickeditem.click_count + 1)
rescue ActiveRecord::RecordNotFound # to avoid error if no identifier value
end

Rails 3 create new child for a specific parent

Hey all! Just joined up on stack overflow, as it has been a helpful resource while starting to learn about Ruby on Rails 3.
I can't seem to find one particular answer though, and maybe I'm barking up the wrong tree for all I know, but hopefully you folks can sort me out here. First some info on what I'm working with:
- In my web app I have 2 models: Projects and Tasks in a one-to-many relationship.
- Projects has many Tasks, and Tasks belong to Project
- Tasks IS NOT a nested resource, as users need to be able to see all current tasks, regardless of which project they are for.
- routes.rb therefore looks like this right now:
resources :projects
resources :tasks
In the project show view I display a list of tasks associated with that project. below that there is a link_to for creating a new task that looks like <%= link_to 'New Task', new_task_path, :class => "new-btn" %>. The link_to takes user to the new view for creating a new task. The rendered _form view starts with: <%= form_for(#task) do |f| %>.
Now, I think I need to pass the project id from the project show view, to the new task view: but, this is where I am getting lost and possibly, a bit mixed up.
Could someone please point me in the right direction: maybe to a resource outlining all steps involved in doing this, or maybe even provide an outline of the steps involved in the process here.
Many thanks!
You may create nested routes and still be able to show all tasks regardless of the project. Just make sure you have task resource defined later, like
resources :projects do |p|
resources :tasks
end
resources :tasks
Now in projects index or show view you can create link like
link_to 'New Task', new_project_task_path(project)
The task index view may be a little bit tricky. All depends on how you sort these tasks. If, for example, you show them sorted by project then you can create a link like
link_to 'New Task', new_project_task_path(task.project)
As for forms. In new action you have to get the id from params and put it into task object
if (params[:project_id])
#task.project_id = params[:project_id]
In form view you may create hidden field that will save this value
f.hidden :project_id
This will make it work, but you will not be able to go to the new task without providing project. Better solution it would be to create a select field with all projects. To accomplish that you should do the following:
in models/project.rb
def getProjectsList
projects = Project.all
projects.map do |p|
[project.name, project_id]
end
end
in controllers/application_controller.rb
def find_projects
#projects = Project.new.getProjectsList
end
in controllers/tasks_controller.rb at the begining
before_filter :find_projects, :only => [:new, :edit, :update, :create]
in views/tasks/_form
f.select :project_id, #projects
This way you can always select project and in case there is one given in params it will be already selected
If current_user returns User object then you should be able to call
current_user.projects
to get all user's projects.
Defining a relation between user and task may be working (though I am not sure this one).
#models/user.rb
has_many :projects
has_many :tasks, :through => :projects
In this case simply
current_user.tasks
should return user's tasks
There are many ways to do it. An easy one may be to add a parameter to you link and use it in the controller:
In your view:
<%= link_to 'New Task', new_task_path(:project_id => #project.id), :class => "new-btn" %>
In your tasks controller:
def new
#task = Task.new(:project_id => params[:project_id])
end
One way to pass the project ID from the project page to the New Task page is to add it to the query string on your url. Example HTML would look like this:
New Task
To get Rails to generate that HTML, you can do this in your ERB:
<%= link_to 'New Task', new_task_path(:project_id=>#project.id), :class => "new-btn" %>
Next, you need to pass the project ID from New Task page to the action that actually creates the Task. One way to do that would be to make a hidden input inside your form that contains the project ID so that it will be passed along with the other parameters when the form is submitted. The HTML would look like:
<input type="hidden" name="project_id" value="<%= params[:project_id] %>" />
To do this the Rails way, you can set the project ID in the new task action in the TasksController:
#task.project_id = params[:project_id]
and then do something like this in your view inside the form_for block (I'm not 100% sure on the syntax):
<%= f.hidden_field(:project_id) %>
Wow! Thanks for all the great info guys! I definitely learned a few neat tricks going through this.
Here is what I have working now:
routes.rb
resources :projects do |p|
resources :tasks
end
resources :tasks
I'm really happy to learn that that part is possible. Now I can enjoy the benefits of nested resource, but use the original non-nested routes for tasks, as well.
the link_to in show project view
<%= link_to 'New Task', new_project_task_path(#project), :class => "new-btn" %>
tasks_controller.rb new action
if (params[:project_id])
#task.project_id = params[:project_id]
end
new task form hidden field
<%= f.hidden_field :project_id %>
That all works great for adding new tasks to projects: but, showing a list of all tasks, related to all projects, that are related to the current user was a little bit trickier, and I wonder if there might be a better way than what I came up with:
tasks_controller.rb in the index action
#projects = Project.find_all_by_user_id(current_user)
#tasks = Array.new
#projects.each do |p|
p.tasks.each do |t|
#tasks << t
end
end
I'm using the "devise" and "cancan" gems for user management(both have been great!). The "current_user" above is simply what you would expect: the currently logged in user. Is this a reasonable solution, or is there a better way of getting all tasks for a user?
Just in case:
User has_many Projects, and Project has_many Tasks

Rails 3 Routes: Named routes. "No route matches"

So I'm getting a 'No route matches' error, and being new to Rails 3 (and Rails in general), I really don't know what the problem is. Here are the pertinent routes:
resources :users
#...
match 'reset_password(/:reset_password_code)' => 'users#reset_password', :as => :reset_password, :via => :get
match 'reset_password' => 'users#reset_password_submit', :as => :reset_password, :via => :post
The GET method works fine. I get a routing error when the form POSTs that's generated on the get page, which starts like this.
<%= form_for #user, :url => reset_password_url do |f| %>
It looks like it's posting to the right spot, as the url is generated using 'reset_password_url', it's posting to it, and the url looks as it should... anyone have any ideas?
UPDATE
I'm using Rails 3.0.4
I've tried taking out every other route except for the ones that I've mentioned here, and I still can't figure out why it's not working.
Figured it out!
In my form, rails was (correctly) assuming that since I had a user that I was using with the form_for helper, that I wanted to update the user, not make a new one.
Therefore, it was using the PUT method to post my form. To solve the routing problem, I just had to change the last route to:
match 'reset_password' => 'users#reset_password_submit', :as => :reset_password, :via => :put
I only found the issue after using the Web Inspector in webkit to see the whole request, and looked at the _method parameter being sent in.