How to optimize my activity feed loop (Rails) - mysql

I am implementing an activity feed for my application, much like the facebook news feed. Whenever a user does something (creates a post, comments on a post, creates a photo album, comments on a photo) a row is created in an Activities table, which contains user_id, category, and data. Data is a serialized hash that is different for each type of activity (post contains post title and post id, photo comment contains something else, etc). The view then loops through all of the activities and prints something depending on the category of activity. Pretty simple, and it works. The problem is that it's SUPER slow, so I must be doing something wrong. Here's the code:
#activity.rb snippet
def post?
category == "post"
end
def post_comment?
category == "post_comment"
end
def album?
category == "album"
end
def photo_comment?
category == "photo_comment"
end
#controller snippet
#Activity = Activity.all(:order=> 'created_at DESC', :limit=>"5")
#view snippet
<% #Activity.each do |a| %>
<div class="activity_item">
<div class="avatar" style="background-image: url(<%= small_pic_url(a.user)%>) "></div>
<div class="text"> <%= a.user.username %>
<% if a.post? %>
posted <%= link_to a.data["post_title"], post_path(a.data["post_id"]) %>
<% elsif a.post_comment? %>
commented on <%= link_to a.data["post_title"], post_path(a.data["post_id"]) %>
<% elsif a.album? %>
created a <%= link_to "Photo Album", album_path(a.data["album_id"])%>
<% elsif a.photo_comment? %>
commented on <%= link_to "#{a.data["username"]}'s photo", upload_path(a.data["photo_id"])%>
<% end %>
</div>
</div>
<% end %>
I added a user_id index on the activity table, but that didn't seem to do much. Rendering the 5 activity items/avatars takes over 3 seconds, so there must be a better way to handle this.

Do you have an index on created_at? If you don't, that'll be what's slowing you down - MySQL will have to read through every row in your activities table to make sure it got the latest five (this applies to just about anything using an ORDER BY clause).
And a couple more suggestions, but these are minor. Might shave a few milliseconds off, but nothing on the order of three seconds:
Include the user when you select your activities. This'll let you get your activities and their users in a single DB query. :include => :user should do the trick, or there's a Rails 3-style .include(...) method, if you prefer.
I've heard that serialization can be kinda slow. Perhaps you could use a polymorphic association, and then choose what to display based on a.whatever_type?
Hope this helps!

Related

How do I get an id from a student in a list and display it the student's information on another page? Ruby on Rails

I am creating a list of students and when I click on one of the hyperlinks, I want to display a table with the student's information I clicked on.
I am working with two pages, index.html.erb and show.html.erb. In the index page, I want to display the list and the show page, I want to display the information for only one student. I am very new to Ruby on Rails, but I want to get better at it. Thank for all your responses.
index.html.erb
<h1> Welcome </h1>
<%= link_to "Display Table", students_show_path %>
<%= link_to "Form", students_new_path %>
<ol> Students
<% #student.each do |s| %>
<!--<li><%= link_to s.FirstName, students_show_path(#student.students_id), method: :post %> </li> -->
<li><%= link_to s.FirstName, :action => "show", :id => Student.id %> </li>
<% end %>
</ol>
This is my show.html.erb
Table's Page
<% #student.each do |s| %>
<tr>
<td> <%= s.FirstName %> </td>
<td> <%= s.LastName %> </td>
<td> <%= s.NickName %> </td>
<td> <%= s.EmailAddress %> </td>
<td> <%= s.Birthday %> </td>
<td> <%= s.MedicalNotes %> </td>
<td> <%= s.Grade %> </td>
<td> <%= s.School %> </td>
<td> <%= s.Gender %> </td>
</tr>
This is my routes.rb
Rails.application.routes.draw do
root 'students#index'
get 'students/index'
get 'students/show'
get 'students/new'
get 'students/update'
get 'students/create'
get 'students/edit'
get 'students/destroy'
get '/signup', to: 'students#new'
post '/signup', to: 'students#create'
post '/index', to: 'students#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :students
# generates:
# get "/students" -- index on your controller
# get "/students/:id" -- show on your controller
# get "/students/new" -- new method on your controller
# post "/students" -- create on your controller
# get "/students/:id/edit" -- edit method on your controller
# put "/students/:id" -- update on your controller
# patch "/students/:id" -- update on your controller
# delete "/students/:id" -- destroy on your controller
end
This is my students_controller
class StudentsController < ApplicationController
def index
#student = Student.all
end
def show
id = params[:id]
#student = Student.where("Student id = ?" , "#{id}")
end
def new
#student = Student.new
end
def update
end
def create
#student = Student.new(student_params)
if #student.save
redirect_to students_show_path
else
render 'new'
end
end
def edit
end
def destroy
end
private
def student_params
params.permit(:FirstName, :LastName, :NickName, :EmailAddress, :Birthday, :MedicalNotes, :Grade, :School, :Gender)
end
end
For your index.html.erb file, try updating the link_to helpers to something like:
<%= link_to s, s.FirstName %>
You don't need to pass the full route + ID if using link helpers with the show method, rails will automagically link for you.
Remove all of the get 'students/... routes, just leave the resources: :students. The routes you're specifying above this line are redundant.
Your show method in the controller can also be cleaned up slightly:
def show
#student = Student.find(params[:id])
end
And finally, I believe in your create method, you can redirect using just the resource:
def create
#student = Student.new(student_params)
if #student.save
redirect_to #student
else
render 'new'
end
First thing:
The Ruby way to name attributes, methods, and variables is to use snake_case. For example, s.FirstName should be s.first_name. In Ruby, if the name is capitalized like FirstName it is actually a constant. This style is called CamelCase and it is the conventional way to name classes in Ruby e.g. StudentsController.
For the routes.rb:
Rails will generate all of the RESTful routes and helper methods for your resource if you use
resources :students
you can then view all of these routes by running rake routes on the command line or visiting http://localhost:3000/rails/info/routes in the browser. You can learn more info about routes in the documentation: http://guides.rubyonrails.org/routing.html#inspecting-and-testing-routes
You can then use the Rails link_to helper method like this:
<%= link_to s.first_name, s %>
and Rails will figure out the id of the object for you. Or if you want to be more explicit:
<%= link_to s.first_name, student_path(s.id) %>
And in the show action of your StudentsController you want:
def show
#student = Student.find(params[:id])
end
this will look up the student record in the database based on the :id parameter in the URL for student show page
Two things I notice about your code
1)
get 'students/show'
To be able to retrieve an user, you need a user id, a unique identifier. So your show route should contain an id. The Rails way is something like this
get 'students/:id'
I'm simplyfing here because your route could work if you call it with your param in query string ('students/show?id=something')
Anyway, you are defining every route in a different line. It turns out that you can define all of them with just the line
resources :students
2)
In your show action you want just one user. And you have an id, and that's unique, so you can use it to directly retrieve your user
def show
#student = Student.find(params[:id])
end
You used where and that's for retrieve ALL the user that match your condition (maybe you want to retrieve every user whose first name is 'John'). So it turns out your #user doesn't contain directly one user, but your user in a relation. That's because where.

Trying to load a view in application layout and calling its action on every page

I am trying to load a view with a list of Product Type. I need the view to be displayed on every page. But the action is not getting called in other pages, please tell me where I'm going wrong and suggest any alternatives.
index.html.erb
<h1>Listing the types of products</h1>
<% content_for :sidebar do %>
<% #types.each do |type| %>
<li><%= link_to type.name, product_type_path(type)%> <!-- go to product_type controller show action-->
<%= link_to "Edit", edit_product_type_path(type) %> <!-- go to product_type controller edit action -->
<%= link_to "Delete", product_type_path(type) , method: :delete, data: { confirm: "Are U Sure?" }%> <!-- go to product_type controller delete action and a pop to confirm the action -->
</li>
<% end %>
<h3><%= link_to "New Type Of Product", new_product_type_path %></h3>
<h3><%= link_to "All Types of Products", products_path %></h3> <!-- All types of products listed for admin's ease -->
<% end %>
This is the Application layout I'm using.
Application.html.erb
<!DOCTYPE html>
<html>
<head>
<%= render 'layouts/title' %>
<%= render 'layouts/rails_defaults'%>
<%= render 'layouts/shim' %>
</head>
<body>
<aside><%= yield: sidebar %></aside>
<%= render 'layouts/header' %>
<%= render 'layouts/flash' %>
<div class="container">
<%= yield %>
</div>
<%= render 'layouts/footer' %>
</body>
</html>
As you can see I'm using yield: sidebar , but its not working properly, in the sense the action index is not getting called if I go to different page.
Product_types Controller
class ProductTypesController < ApplicationController
def index
#types = ProductType.all #to get all the records to an instance variable(array in the case) lasts only until the scope lasts. Since the is defined in index, it lasts only till you are using the index view
end
def new
#type = ProductType.new #to create a new record into the respective model
end
def show
#type = ProductType.find(params[:id]) #Finding the type of product click on
#products = Product.where(value: #type.value) #finding all the products whose value field is same as the type of product value(primary key)
end
def create
#type = ProductType.new(type_params) #type params defined below in private class
if #type.save #save the product created
redirect_to root_url #and redirect to root
else #if doesnt save and error occurs
render 'new' #error occurs and render 'new' view
end
end
def edit
#type = ProductType.find(params[:id])
end
def update
#type = ProductType.find(params[:id])
if #type.update(type_params) #update the params
redirect_to root_url #if updated redirect to root
else
render 'edit' #else if error occurs render 'edit' view again
end
end
def destroy
ProductType.find(params[:id]).destroy #destroy the record
redirect_to root_url #redirect to root
end
private
def type_params
params.require(:product_type).permit(:name,:value) #used to only permit type and value thorugh request(to prevent hacking) used in create and update action above
end
end
The action is not getting called whenever i jump to another page.
please suggest alternatives.
content_for / yield are for the current view only invoked by just the current action. So if the other pages dont have a content_for :sidebar in their views, then they wont have a sidebar. And if you just include in the content_for directly, it still wont execute any extra controller logic required.
Use a helper or partial like your layouts/header if you want resuable content, rather than a reusable "space".
For things you dont want to do only in a partial, but also not only in direct Ruby (with content_tag, etc.), you might combine a partial and a helper.
class ApplicationHelper # Or any other helper
def products_sidebar
products = Product.where(show_on_sidebar: true).order(:popularity) # Or whatever you like
render partial: "shared/products_sidebar", locals: {products: products}
end
end
shared/_products_sidebar.html.erb (Id definitely give consideration to other template engines)
<div id="products_sidebar">
<% products.each do |product| %>
<div><%=product.name%></div> <!--whatever you want it to look like-->
<% end %>
</div>
Then in your index, you can just call it, and it wont depend on the action/controller currently being processed.
<body>
<aside><%= products_sidebar %></aside>

User index show users multiple times

I've got a total of 11 users in my seeds.rb. however, in my user index, it displays 11x11 = 121 users. each user is listed 11 times. Also, whenever I create a new user on the website (sign up), it keeps on multiplying. +1 signed up user leads to a total of 12 users in the db and 12x12 = 144 users being displayed (THEY ARE NOT IN THE DB!) in the index... what's wrong here?! I checked my users controller create action for any weird stuff but couldn't find anything out of the usual.. any help?
this is my user index view:
<% provide(:title, 'Users') %>
<h1>List of Users</h1>
<%= will_paginate %>
<ul class="users">
<% #users.each do |user| %>
<%= render #users %>
<% end %>
</ul>
<%= will_paginate %>
As Solarflare mentioned in the comment, you're rendering the entire user list (#users) at every iteration of that loop. You should update your loop to be:
<ul class="users">
<% #users.each do |user| %>
<%= render user %>
<% end %>
</ul>
That way you're rendering the page for each individual user in the loop.

Displaying data from another two different tables on an index page

I'm struggling to get data from two separate tables to display on the same index page.
I have data from one table displaying as a list this table is called "transactions".
Now in my model for transactions I have defined that it has many "notes. which is my other table I want to display data from on this same index.
I want each transaction to display the notes associated with it by the id of the transaction and the id in the notes table under transaction_id.
For the life of me I have yet to figure out how to do this.
Here is what my transactions index.html.erb looks like:
<% #transactions.each do |transaction| %>
<div class="element">
<%= trash_ico(transaction) %>
<%= edit_ico(transaction, edit_transaction_path(transaction)) %>
<%= link_to "<h2><span class=\"el_header\">#{Asset.find(transaction.asset_id).nmc_name} : Assigned to #{User.find(transaction.user_id).name}</span></h2>
<div class=\"el\">Created: #{transaction.created_at}</div>
<div class=\"el\">Ended: #{transaction.finished}</div>
<div class=\"el\">Type: #{if ! transaction.transaction_type.nil? then transaction.transaction_type.name end}</div>
<div class=\"el\">Status: #{if ! transaction.transaction_status.nil? then transaction.transaction_status.name end}</div>
<br clear=\"all\" />".html_safe, transaction %>
<% note.each do |note| %>
<%= link_to "<div>#{note.notes}</div>".html_safe, note %>
<% end %>
</div>
<% end %>
And this is how the controller is defined for the index:
def index
unless params[:asset_id].nil?
#transactions = Transaction.find_all_by_asset_id(params[:asset_id])
else
#transactions = Transaction.all
end
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #transactions }
end
end
I'm very green to programming but mostly to rails. I see what the task is that I need to do, but I'm not sure what the syntax must be to do it.
I have a similar view setup for my show def. I just can't get notes to show underneath their respective transaction on the index.
I am thinking, something like this;
<% transaction.notes.each do |note| %>
<%= link_to "<div>#{note.notes}</div>".html_safe, note %>
<% end %>
Since you declared notes belonging to transactions. Rails allows you to access them this way.

Is there a best practices/coherent way to update a database field that contains a hash key-value store?

I'm referring to Rails 3.2's Data Store feature, in which there's the option to store key-value stores in a textfield, even if you're using a relational database like MySQL...it works fine when programmatically manipulating the fields.
But what documentation is there to update these fields from a RESTful HTML form? Or is this something that's not recommended at all? That is, the better solution would be to go to NoSQL?
If I understand the question, I think you just need to declare the field name holding the store, and the associated accessors (properties) in the model, like
store :settings, accessors: [ :weight, :length, :color ]
at which point the field works with AR and AREL just like any other, even with forms.
There's very little magic here. The field holds a hash of values; the store declaration lets Rails know that you can reference them like something.weight or something.color, whether reading or writing. Simple and slick. Classic DHH.
although the question is quite old someone else might find it useful, also im pretty new in ruby and rails so there might be a better way to do this.
In the model:
#user.rb
attr_accessible :preferences
store :preferences
then in the form partial:
#views/users/_form.rb
<% #user.preferences.each do |k, v| %>
<% form.fields_for :preferences, #user.preferences[k] do |p| %>
<div class="field">
<%= p.label k %>
<br/>
<%= p.text_field k, :value => v %>
</div>
<% end %>
<% end %>
Now to add some extra fields from the form ive created 2 attr_accessor in the model:
attr_accessible ... , :new_pref_key, :new_pref_val
attr_accessor ... , :new_pref_key, :new_pref_val
then added the 2 new fields on the form
<%= f.label :new_pref_key %>
<%= f.text_field :new_pref_key %>
<%= f.label :new_pref_val %>
<%= f.text_field :new_pref_val %>
on my controller i made a function that check the presence of the new fields and then merge the previous values of the prefs with new ones, like this:
#users_controller.rb
...
new_key = params[:user][:preferences][:new_pref_key]
new_val = params[:user][:preferences][:new_pref_val]
new_preference = {
new_key => new_val
}
current_params = params[:user][:preferences].merge! new_preference
...
done that i return it and pass it to the update_attributes, hope it helped!