I'm trying to make a class to populate a deals tab on my website.
Part 1. Take an items close date (CatalogItem.close_date) and use all items within 12 hours of closing. Part 2. Calculate the deal percentage by using the current price (CatalogItem.current_price) and estimated value (Item.estimated_price) <-- You'll notice they're in different tables but they're identified by an identical item_id.
I'm green in RoR, so I'm having trouble connecting this in a class, but I can make it work individually in the console:
hour_diff = (CatalogItem.last.close_date - Time.now) / 1.hour
deal_percentage = (CatalogItem.last.current_price.to_f / Item.last.estimated_price)*100
As you can see I'm using my .last piece of data, but I want to create an array that runs through all my items, that's where my knowledge goes dry, any help would be much apreciated
I'm assuming you are using a belongs_to, but I think what you want to do is use
an instance method. This would be your model, app/models/catalog_item.rb
class CatalogItem < ActiveRecord::Base
belongs_to :item
def hours_remaining
(close_date - Time.now) / 1.hour
end
def deal_percentage
(current_price.to_f / item.estimated_price)*100
end
end
Then, you could access them in a view something like this:
<table>
<% CatalogItem.all.each do |ci| %>
<tr>
<td><%= ci.hours_remaining %></td>
<td><%= ci.deal_percentage %></td>
</tr>
<% end %>
</table>
Related
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
I am a newbie at it.I have just educated myself for 2 days. And a have a problem.
Example: I have a table , called as tblData, includes 2 columns: id, img_link. img_link contains link to an image.
I want to show all of them in this table(id and image, not image link) into a html file.
So, exactly what I need do?
If you have an image link that you can pull from your database, you can do something like this:
Assuming you have an object assigned and everything's set up:
In your controller for tbl_data (e.g. app/controllers/tbl_data_controller.rb):
class TblDataController < ApplicationController
def your_action
#tbl_data_item = TblData.first
end
end
(The code above is just an example, you should substitute for whatever code/query you wish to run)
In your view template, you can render an image from a link using the following Rails view helper:
<%= image_tag(#tbl_data_item.img_link) %>
This would output the following HTML:
<img src="/path/to/image/from/img_link" />
There's a lot more info on this helper on the Rails api docs. The Rails Guides has some awesome info on getting things set up and running as well. Hope this helped!
UPDATE:
To give you a better example with clearer steps, you would do something like the following:
Set up your routes (app/config/routes.rb):
Rails.application.routes.draw do
resources :tbl_data
end
Create your model, used to communicate with its respective database table (app/models/tbl_data.rb):
class TblData < ActiveRecord::Base
# your model-specific code goes here - validations, scopes, etc.
end
Create the controller, which responds to when a user hits a certain route in your app (app/controllers/tbl_data_controller.rb):
class TblDataController < ApplicationController
def your_action
#tbl_data_items = TblData.all
end
end
Create the view template, that will be rendered for your user(app/views/tbl_data/your_action.rb):
<table>
<% #tbl_data_items.each do |item| %>
<tr>
<td><%= item.id %></td>
<td><%= image_tag(item.img_link) %></td>
</tr>
<% end %>
</table>
The above would show a table with each record in the #tbl_data_items as a row, with 2 columns, one with the id, and one with the actual image for that item.
Create a folder in public called images, put the desired image in that folder.
Put <%=image_tag 'whatevertheimagefilenameis.png', class: 'example-class', id: 'example-id'%> in the html.
There are
a user-model/table,
a schedule-model/table and
a users_schedules-table.
The models has_and_belongs_to_many each other.
I can add a relationship with #user.schedules << #schedule in the controller.
How can I get access to the join-table 'users_schedules' ?
I want to show the users which has_and_belongs_to_many schedules:
I thought about something like this: schedules.users_belongs_to. As you can see in the view-code below.
view: (There is an example I want to add)
<table class="table table-hover">
<tbody>
<% #user_schedules_date.sort.each do |date_time, schedules| %>
<tr class="thead success">
<th colspan="4" scope="col"><p><%= date_time.strftime("%A, %d.%m.%Y") %></p></th>
</tr>
<% for schedule in schedules %>
<tr>
<th scope="row"><p><%= schedule.titel %></p></th>
<td><p><%= schedules.users_belongs_to #ALL USERS WHO ARE BINDED TO THIS SCHEDULE# %></p></td>
<td><p><%= schedule.date_time.strftime("%H:%M:%S") %></p></td>
<td><p><%= schedule.location %></p></td>
<td>
<p>
<%= link_to 'Bearbeiten', {:controller => 'schedule', :action => 'edit', :id => schedule.id} %>
oder
<%= link_to 'löschen', {:controller => 'schedule', :action => 'delete', :id => schedule.id} %>
</p>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
In the controller I tried the following, but I don't know how to replace/fit the placeholder (:email).
controller:
class WelcomeController < ApplicationController
def index
if(current_user)
#user_schedules = current_user.schedules
#user_schedules_date = #user_schedules.order(:date_time).group_by { |sched| sched.date_time.beginning_of_day }
#users_schedules_shared = User.find_by(:email) #HERE I NEED THE USER WHICH BELONGS_TO THIS SCHEDULE
end
end
end
I hope you can understand my problem.
Thanks for your help!
EDIT:
I gather all the data in the controller:
class WelcomeController < ApplicationController
def index
if(current_user)
#user_schedules = current_user.schedules
#user_schedules_date = #user_schedules.order(:date_time).group_by { |sched| sched.date_time.beginning_of_day }
#users_all = User.includes(user_schedules: :schedules)
end
end
end
and edit the view as the following:
<% #users_all.each do |user| %>
<% user.name %>
<% end %>
But I get the following error:
Association named 'user_schedules' was not found on User; perhaps you misspelled it?
I red this, as deyan said, but I dont understand it.
#users_all = User.includes(user_schedules: :schedules) <- returns an array ?!?? (If I understood it correctly)
So I need each array-item.name to show the Users name??
Can anyone tell me what I am doing wrong?
Database:
http://i.stack.imgur.com/Apg7y.png
users
schedules_users (join-table with fk)
schedules
I want to show the users which has_and_belongs_to_many schedules
This will use something called a many-to-many relationship - meaning that if you access the associated data through a model - you'll have an appended attribute / method to capture them.
So what you'd do is the following:
#app/models/schedule.rb
Class Schedule < ActiveRecord::Base
has_and_belongs_to_many :users
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_and_belongs_to_many :schedules
end
This will append a collection to each of these model objects, allowing you to call the collection as required:
#schedules = Schedule.all
#schedules.each do |schedule|
schedule.users #-> outputs a collection
schedule.users.each do |user|
user.name
end
end
Using includes with Rails is actually quite a bad thing, considering you can call ActiveRecord associations to do the heavy-lifting for you
--
has_and_belongs_to_many
Simply, you can't access the has_and_belongs_to_many table directly, as it has no primary_keys in place.
Rails basically uses the relational database infrastructure (through ActiveRecord) to access the associative data. This means that if you're using this particular type of table, you'll just be able to access the collection it provisions:
#app/models/user.rb
Class User < ActiveRecord::Base
has_and_belongs_to_many :schedules
end
#app/models/schedule.rb
Class Schedule < ActiveRecord::Base
has_and_belongs_to_many :users
end
This will allow you to access:
#user = User.find 1
#user.schedules #-> shows schedules collection. You will have to loop through this
If I understand you correctly, you want to show the schedules for your current user, and for each schedule to show what other users belong to it.
Based on your view, to get all users for a schedule, all you would need is to replace schedules.users_belongs_to withschedule.users.
If you want to get together all unique users for all the schedules the current user might have, then in the controller you could do current_user.schedules.collect{|s| s.users|}.uniq. This is only useful if you want to show all users, no matter which schedule each user belongs to.
I would advice you to gather all the data in the controller and then to print it in the view. You could join all tables in a single call to the DB, which might look something like this: User.includes(users_schedules: :schedules) but you would need to adapt it depending on what your models are called.
Methods 1 or 2 are solving your problem using your current code, but might be slow. I would suggest you read more here: http://guides.rubyonrails.org/active_record_querying.html and get all data at once (method 3).
In my web application the user can select certain instances of an entity. For instance on the class BubbleGum, now the user can select certain instances of BubbleGum by adressing their identifier:
gums/details?show=3532667
Now, in addition I also want to make it possible to display all BubbleGums. For this I have introduced the convention of using * to identify all
gums/details?show=*
This works nice so far, but often I have to add a bit code to process the * selection. Is there a nice way to represent an all-instances object in Ruby and/or Rails?
I have thought of using simply a dedicated symbol, constants, or an actual object of the class BubbleGum that represents all the other bubble gums.
To display all the entities in a rails application generally we use a index page.
bubble_gums_controller.rb
def index
#bubble_gums = BubbleGum.all
end
views/bubble_gums/index.html.erb
<% #bubble_gums.each do |bubble_gum| %>
<tr>
<td><%= bubble_gum.name %></td>
<td><%= bubble_gum.price %></td>
</tr>
<% end %>
Refer this for further details.
http://guides.rubyonrails.org/getting_started.html#listing-all-posts
I think you want to use the query string param show.
So, you can try in your gums controller:
def details
if params[:show] == "*"
#bubble_gums = BubbleGum.all
# ...
elsif params[:show]
#bubble_gum = BubbleGum.find(params[:show])
# ...
else
render :status => 404
end
end
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) %>