Different behavior of "each" method in development and production - ruby-on-rails

I have table in my ERB-template. Like this:
<table>
<% #users.each do |user| %>
<tr>
<td><%= user.id %></td>
<td><%= user.name %></td>
</tr>
<% end %>
</table>
It works fine. Suppose I have 2 users.
Table will be like this:
1 | foo
2 | bar
But for example, I decide to edit user with id1.
In development it will be correct. Like this:
1 | oof
2 | bar
But in production it will be:
2 | bar
1 | oof
After editing user always jumps to the end of table.
I think it because I use SQLite in local but Heroku uses PostgreSQL.
I'm newbie, so I can be wrong.
&nbsp
My question is: how should I change my code to make the code work correctly everywhere?

Explicitly specify order in your controller. If the order is unspecified, PostgreSQL does not guarantee any order, so assume it will be randomized.
#users = User.order(:id)#.limit/pagination
I would also advise you to have the same databases in development and production, if you take some time to setup PostgreSQL locally, it will pay off.

Related

Index view loading very slowly

I have a model Schools and a model PerformanceStats.
PerformanceStat
belongs_to :school
School
has_one :performance_stat
the index page for PerformanceStat shows all 2,000 performance stats, and also the school.name, school.score, and school.city, and I need access to the school.id and school.slug.
Controller:
def index
#performance_stats=PerformanceStat.all
end
My view code:
<tbody>
<% #performance_stats.each do |stat| %>
<% school = School.find(stat.school_id)%>
<tr>
<td><%= link_to school.name, school_path(city: school.city.parameterize.truncate(80, omission: ''), slug: school.slug) %></td>
<td><%= number_with_precision(school.score, precision: 2)%></td>
then the view goes on to display the performance stats.
This view load very slowly....10-20 seconds. How can I speed things up? I've tried PerformanceStats.scoped, and plucking school stats and selecting from an array, but these don't seem to help. Is there a way for me to access the school attributes without finding a School for every PerformanceStat? I believe the School.find bit is slowing things down considerably.
I have indexes on :school_id in PerformanceStat, and :score, :slug in the School model.
UPDATE:
The suggestion in the selected answer to add a cache resulted in this line of code in the index action of the SchoolsController:
fresh_when etag: #performance_stats
The load time dropped to 18ms. This solution works great for me because the content of the index action does not change often. This data gets updated once a year. This link has other suggested cache solutions for data that changes frequently.
PerformanceStat.all is a heavy query if you've a lot of data in this table and it'll be finding school for each performance stat.
What I can understand from your code is that you're facing (N + 1) problem over here.
NOTE: you should not fire queries from your views or helpers and let the controller do all the action.
For instance in your code:
<% #performance_stats.each do |stat| %>
<% school = School.find(stat.school_id)%> <- #THIS IS WRONG & LET THE ASSOCIATIONS DO ALL THE ACTION ON ITS OWN
<tr>
<td><%= link_to school.name, school_path(city: school.city.parameterize.truncate(80, omission: ''), slug: school.slug) %></td>
<td><%= number_with_precision(school.score, precision: 2)%></td>
you can use includes, PerformanceStat.includes(:school) it will fetch all the schools for each PerformanceStat.
your controller code should be:
#performance_stats = PerformanceStat.includes(:school)
instead of : #performance_stats = PerformanceStat.all
and your view code will now be:
<% #performance_stats.each do |stat| %>
<% school = stat.school %> #make sure all stats have a school assigned to them otherwise you can put a check below whether the school is nil or not
<tr>
<td><%= link_to school.name, school_path(city: school.city.parameterize.truncate(80, omission: ''), slug: school.slug) %></td>
<td><%= number_with_precision(school.score, precision: 2)%></td>
Quite a few things here. First of all change your controller method to this one, otherwise you will run into n+1 queries
def index
#performance_stats=PerformanceStat.includes(:school)
end
Since you have eagerly loaded the school, now you can access it directly in your view as
<% stat.school %>
Secondly loading almost 2000 records in one go is not optimal at all, it's gonna take a while to load all records. For this you must add pagination by using following gems
kaminari
will_paginate

Using Heroku, Rails sort is incorrect on updated_at timestamp column

I have a Rails 4.0 APP using PostgreSQL on Heroku. I am trying to display a table that is my XLog or transaction log, showing the last five entries in reverse order by updated_at timestamp. This works correctly on my local system. When I push it to Heroku, it sorts incorrectly.
I have checked the Heroku database definitions and the column is correctly listed as a timestamp. I have cloned the Heroku code back to my machine and verified that it is the same as what I pushed. At this point, I don't know why it doesn't work on Heroku when it works locally. And advice would be appreciated.
FWIW, the remote database and local database do not have the same data.
The code is: (Last line of log_sort was added to act as a breakpoint that would still pass the correct result.)
def self.last_objects object, count
logs = XLog.where(object: object).last(count)
log_sort = logs.sort_by{|log| log.updated_at}.reverse
log_sort
end
During execution to the breakpoint, you can see the variables passed:
This is the local result with the correct sort:
This is the Heroku result with the incorrect sort:
This is the Heroku PostgreSQL table definition for updated_at:
EDIT: View:
<% xlog = xlog_last(car.stock_number, 5) %>
...
<% xlog.each do |log| %>
<tr>
<td><%= log.associate %></td>
<td><%= log.proxy %></td>
<td><%= log.action %></td>
<td><%= log.status %></td>
<td><%= log.message %></td>
<td><%= log.value %></td>
<td><%= log.updated_at %></td>
</tr>
<% end %>
Helper:
def xlog_last(object, count)
XLog.last_objects object, count
end
EDIT:
I modified the sort_by to use an order method as follows. The results did not change at all. The same sorting error occurred and the exact same data was displayed in the exact same way:
New code:
def self.last_objects object, count
logs = XLog.where(object: object).order(updated_at: :desc).first(count)
end
I believe you need to use order to influence the actual sql query. Right now you are using sort_by which is sorting the data after you read in from the database. The order in the db is based on how it was inserted and could be different from heroku and your local system. When you export from heroku, and then import it, the ordering of the tables probably changes too.
A gem was incorrectly sorting the view. Correcting that issue fixed the problem. Still unsure why it didn't show in test except possibly for the data differences.

Rails Scope, Helper Method?

I have three models. One is an Employee, one is an Item, and one is a Transaction that belongs to both Employee and Items. It's a simple app that allows Employees to check in and check out items - 'Transaction' has a boolean column for checked-in/checked-out.
What I'm trying to do is show within the employee/show view the current list of Items that an Employee has checked out. This is some rough code that I sketched out, but I'm not sure that it's going to work, and I was told not to use a lot of nested conditionals in my views anyway.
<% if #employee.transactions.exists? %>
<h3>Currently Checked-OUT Items</h3>
<table>
<tr>
<th>Item Asset Tag</th>
<th>Item Description</th>
</tr>
<% #employee.transactions.each do |transaction| %>
<% if item.transaction.last? && transaction.status == false %>
<tr>
<td><% transaction.assettag %></td>
<td><% transaction.description %></td>
</tr>
<% else %>
NO CHECKED OUT ITEMS
<% end %>
</table>
<% end %>
<% end %>
Basically, I'm trying to:
checks all employee transactions
compares the item involved in the transaction and sees if it's the .last transaction record for item
if it is, and if it's false, then it's a current checkout.
Is this a better job for a scope within the Transaction model, or a helper method? I've never used either, I'm really new at rails.
You should do a couple of things in here.
First - create a scope that will fetch last item transaction for you. There's no point in going through al item transactions if you're interested in the last one only, right?
Second, use partials. In this example it's hard to show how I would refactor code to use them (some things doesn't make sense here, ex. where does item variable come from?)
Scope example (take last transaction)
#item.transactions.order('created_at DESC').first
You can as well add scopes for checkin / checkout
class Transaction
scope :checkin, -> { where(status: true) }
scope :checkout, -> { where(status: false) }
end
First, you are on the right track. When views get ugly and hard to read because of extensive embedded ruby conditionals and such, think about moving the logic into a helper.
If you have a typical rails app, you'll already have app/helpers/application_helper.rb
So you could just create a helper in that file
def make_employee_list(employee)
if employee.transactions.exists?
content_tag(:div) do
content_tag(:h3, "Currently Checked-OUT Items")
content_tag(:table) do
employee.transactions.each do |transaction|
# you get the idea
end
end
end
end
end
Then in your view you could do this:
<%= make_employee_list(#employee) %>

Remove entry from a table without deleting it from a database

I've built a small web app in which main view shows a table and administrator has an option to remove a row by deleting it from a database using:
*.destroy
However I want to keep all the entries in the database but still would like the option for the user to be able to remove the rows from the table and not sure how to go about this. I was thinking about using two different database tables but wanted to check if there is maybe a simpler way?
Here is my main view:
<h1>Student List</h1>
<table class ="Tables">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Date Submit</th>
<th>Floor Preference</th>
<th></th>
</tr>
<% #students.each do |student|%>
<tr>
<td><%= student.f_name %></td>
<td><%= student.l_name %></td>
<td><%= student.created_at %></td>
<td><%= student.floor_pref %></td>
<td><%= button_to 'Remove', student, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New Student', new_student_path %>
you can easily do this by creating an extra column in same table with boolean value.
So extra column name can be isActive.
Removed row's isActive would be 0 and rest have 1.
And when fetching data just put extra check where isActive = 1
rails g migration AddActiveToStudents active:boolean
Then in view you can alter the value when they click the "Delete" button
Then put a check after the .each do |student|
<% if student.active %>
..........display..............
<% end %>
This solution is a little more labor intensive, but works for me. My steps are similar but I'll provide more detail because I'm new to programming so I'm going to assume there are others who are new as well:
Assumptions:
- You're working from a single model, views and controller (e.g. Model is Foo)
- You're able to delete a record from the db by clicking on a button that initiates a controller action ("Destroy")
- If you've got associations, destroying a record from the db is problematic to your app.
Created a migration for new column named is_active, w/datatype: boolean. Verify that rails created the migration correctly. If not, manually tweak the migration file. Make sure you bundle exec rake db:migrate after ANY change to the db. Restart your server.
Edit the destroy action in the controller by setting the is_active variable to false. Boolean evaluates to 'true' or 'false'. For us 'false' will represent a "deleted" row (although it won't be deleted in the database, just hidden from the view. e.g. #foo.is_active = false. Be sure to use the "=" operator, which assigns the value. In the view you'll use a different operator (==). Make sure you do a save after any editing. e.g. #foo.save. Also, comment out any destroy code. e.g. # #foo.destroy
2a. I also set the is_active value to 'true' in my create action. This may not be necessary but it works for me so far. If you made this adjustment, remember to save. e.g #foo.is_active = true, #foo.save
In the view where your results will show, add a conditional statement. I'm looping through my table and painting results. My if statement is after my do loop. If it evaluates to 'false' I'll show a line of text. ELSE, if it evaluates to 'true' I'll show my data. (Generally, on subsequent lines I have: Do Loop, 'False' iF Statement, Else, 'True' Data , End tag).
So, in the "if" statement I'm telling rails that when you're looping thru, if you see a row where the foo.is_active is 'false', show this text. Otherwise (else) show the data between 'else' and 'end'. Notice how the operator in the view is different than the controller. In the view we're checking for equality (==), not assigning a value to (=).
Make sure the variables that you're calling match what you've set in your controller's destroy action. They must be consistent or you'll get error messages. This gave me problems for a few hours before I figured it out. So if you're setting #foo in your controller, then you can only call #foo in your view.
Finally, the view I'm working with is my index but I also but a conditional statement in my show view so my users can toggle is_admin on or off. Using a simple_form_for form, I added the is_admin variable as boolean which shows as a checkbox on my show form. When checked is_admin is 'true' and the row will show. When unchecked is_admin is 'false' and therefore hidden.

Sorting issues on Heroku

I've got the following that works fine on my local machine (running a MySQL DB) but on Heroku the sort order is wrong, instead of 1,2,3,4,5...11,12,13 etc I get 1,11,12,13,2,3,4,5...!
<% #release.releases_tracks.sort { |a,b| a.position <=> b.position }.each do |releases_track| %>
<tr>
<td><%= releases_track.position %></td>
<td><%= releases_track.track.name %></td>
<td><%= releases_track.track.artists.map { |a| a.name}.join (", ") %></td>
<td><%= releases_track.track.isrc %></td>
</tr>
<% end %>
I thought is was because my position column was a varchar, but i've changed to integer, migrated the db on Heroku and it's still doing it! What's going on?
It looks like the column is still varchar or string. Can you get on the console on Heroku to load an object from the table and inspect the field to make sure if it has indeed changed to integer?
At first, I thought this might have been caused by the difference between mySql and Postgres that Heroku uses, but I do not think that's the case here.
Also, you could use ActiveRecord to handle the sorting...
results = YourModel.where('blah...blah..').order('id desc')

Resources