Rails 3 : Matrix Grid View - ruby-on-rails

I need to display multidimensional array data in a table, like so:
for an sla.expected_billing, there is a grid with 12 periods down the left side (rows)
7 ledger numbers across the top (columns)
amounts in applicable fields, but there may be null values
For instance,
Period 1: Ledger 1 = 400.00, Ledger 2 = null, Ledger 3 = 250.55, Ledger 4 = 500, etc..
Period 2: Ledger 1 = null, Ledger 2 = null, etc...
class ExpectedBilling < ActiveRecord::Base
belongs_to :sla
belongs_to :period
belongs_to :ledger
end
class Period < ActiveRecord::Base
has_many :expected_billings
end
class Ledger < ActiveRecord::Base
has_many :expected_billings
end
class Sla < ActiveRecord::Base
has_many :expected_billings
end
I was thinking the Matrix library and/or Class.transpose might be what I need, but I'm not sure. When there may be null values in any given array, I'm afraid it won't populate the grid correctly, either.
Can anyone point me in the right direction?

I would model it slightly differently:
class Sla < ActiveRecord::Base
has_one :expected_billing
end
class ExpectedBilling < ActiveRecord::Base
has_many :periods
belongs_to :sla
end
class Period < ActiveRecord::Base
has_many :ledgers
belongs_to :expected_billing
end
class Ledger < ActiveRecord::Base
belongs_to :period
end
To print your table in a view:
<table>
<!-- insert header row here -->
<% sla.expected_billing.periods.each do |period| %>
<tr>
<!-- insert header column here -->
<% period.ledgers.each do |ledger| %>
<td><%= ledger.value %></td>
<% end %>
</tr>
<% end %>
</table>

Related

Ruby/Rails 6 summation during iteration

I've got an App in which User can buy shares in a Portfolio through a wallet.cash_transaction. Now I would like to display all shareholders of the portfolio specifying the user's name and the number of shares in a given portfolio.
#models association
class User < ApplicationRecord
has_one :wallet, as: :walletable
has_many :cash_transactions, through: :wallet
end
class Portfolio < ApplicationRecord
has_one :wallet, as: :walletable
end
class Wallet < ApplicationRecord
belongs_to :walletable, polymorphic: true
has_many :cash_transactions
end
class CashTransaction < ApplicationRecord
belongs_to :wallet
belongs_to :to_wallet, class_name: 'Wallet', optional: true
end
To display shareholders I'll need below join:
> portfolio = Portfolio.last
#shareholders = User.joins(:cash_transactions).where(cash_transactions: { to_wallet: portfolio.wallet }).uniq
Which I can use in the view by below iteration:
<tbody>
<% #shareholders.each do |user| %>
<tr>
<td><%= "#{user.first_name} #{user.last_name}" %></td>
<td><%= user.cash_transactions.where(to_wallet: portfolio.wallet).sum(:shares_number) %></td>
</tr>
<% end %>
</tbody>
Line user.cash_transactions.where(to_wallet: portfolio.wallet).sum(:shares_number) is responsible for the summation of all user shares in the portfolio. This method doesn't seem very efficient to me because it seems like I'll be sum-up unnecessarily at each iteration. I imagine that when there will be hundreds of thousands of users it can be aggravating at every refresh of the page. Is there a better way to do so?
You can write SQL queries to get sum of all matched records
#shareholders = User.joins(:cash_transactions)
.where(cash_transactions: { to_wallet: portfolio.wallet})
.select("users.*, SUM(cash_transactions.shares_number) as total_shares_number")
.group("users.id")
you will get all columns of users table and sum of total shares_number, Add more fields form another table as per your requirement

Rails display name attribute from different table along with count from join table

I have the following tables: Products, Categories, ProductCategory.
I'm trying to display in a branch structure something like:
Accessories - 5,000
Men's Watches - 2,000
Tag Huer - 1,000
Samsung - 1,000
Women's Watches - 3,000
The issue I'm getting into is that my query is slowing down the application. I have the following query:
def category_level
Connector::Category.includes(:products).group_by(&:parent_category_id)
end
Then in my views I have the following:
<ul class="list-style-upper">
<% category_level[nil].each do |root| %>
<%= render 'products/submenu/category_item', category: root %>
<% end %>
</ul>
Which loads into:
<li class="list-one">
<%= category.name %><p><%= category.products.count %></p>
<% if category_level[category.id].present? %>
<ul class="list-style-upper-sub">
<% category_level[category.id].each do |child| %>
<%= render 'products/submenu/category_item', category: child %>
<% end %>
</ul>
<% end %>
</li>
It displays but takes a long time due to it hitting the Products table and looping through all the products. So to make it easier I thought I'd just hit the ProductCategory page to get a count with the following:
def level_up
#category_counts = Connector::ProductCategory.group(:category_id).count
end
This will actually just display the following:
{54 => 11, 29 => 14, 51 => 19, 10 => 3202}
Although yes 10 would represent Accessories I'd rather see Accessories - 3,202.
Any advice on cleaning this up to pull in the attribute of name?
Provided that your models look something like:
class Category < ApplicationRecord
has_many :product_categories
has_many :products, through: :product_categories
end
class ProductCategory < ApplicationRecord
belongs_to :product
belongs_to :category
end
class Product < ApplicationRecord
has_many :product_categories
has_many :categories, through: :product_categories
end
You can get a count of the associated items by joining and selecting a count of the joined table:
Category.left_outer_joins(:product_categories)
.select('categories.*, count(product_categories.*) AS product_count')
.group('categories.id')
.left_outer_joins was added in Rails 5. For earlier versions use:
.join('LEFT OUTER JOIN product_categories ON product_categories.category_id = category.id')
You can also use .joins(:product_categories) if you don't care about categories without any products.
The count will be available as .product_count on each record.
This can also be achieved by adding a counter cache:
class ProductCategory < ApplicationRecord
belongs_to :product
belongs_to :category, counter_cache: true
end
class AddProductCategoriesCountToCategories < ActiveRecord::Migration[5.2]
def change
add_column :categories, :product_categories_count, :integer, default: 0
end
end
This will store a count in categories.product_categories_count. Counter caches are best used when you have more read than write operations.

NoMethodError - Undefined Method - Pulling a name out of an ID in Rails 4

I have a landlord model and in the table there is a field for listing_agent_id. There is also an agent model where all of their info is stored. In the index view I am trying to us <%= landlord.listing_agent.name but keep getting an error. I have defined agents in my landlords_controller, but it still doesn't seem to be working. Any help would be appreciated.
Landlord Index:
<tbody>
<% #landlords.each do |landlord| %>
<tr>
<td><%= landlord.listing_agent.name %></td>
</tr>
<% end %>
</tbody>
Landlords Controller:
def index
#landlords = Landlord.all
end
def new
#landlord = Landlord.new
#agents = Agent.employees.order(first_name: :asc)
end
Landlord Model:
class Landlord < ActiveRecord::Base
has_many :landlord_addresses
end
Error:
ActiveRecord does not "automagically" create an association just because you have a *_id column. There are just two many possibilities for that to be remotely useful.
To setup an association between Landlord and Agent you would do:
class Landlord < ActiveRecord::Base
belongs_to :listing_agent, class_name: 'Agent'
inverse_of: :landlord
# use inverse_of: :landlords if the relation is one to many.
end
class Agent < ActiveRecord::Base
has_one :landlord, inverse_of: :listing_agent
# or
has_many :landlords, inverse_of: :listing_agent
end
The class_name: 'Agent' option is needed because ActiveRecord cannot deduce the class from the name of the association. inverse_of helps avoid inconsistencies by keeping a single object in memory.

Collection_check_box usage in RoR

I'm a relative novice to rails. I have a process that runs and finishes with a status (five possible different statuses). The process is run per-building. (We collect data from buildings.) I'm trying to allow users to configure themselves to receive notification email with fine-grained control: Per-building and complettion status. I'm trying to use collection_check_boxes to create a table of checkboxes for the user to select, but I'm not even sure collection_check_boxes was designed for such a case. I would be very happy to hear a yes or no on that question to start with.
I have the following modles:
class Building < ActiveRecord::Base
self.primary_key = 'building_id'
has_many :etl_status
has_many :email_notifications
end
class BuildingUserPair < ActiveRecord::Base
self.primary_key = "building_user_pairs_id"
belongs_to :building
belongs_to :user
has_many :email_notification_settings
end
class EmailNotificationSetting < ActiveRecord::Base
self.primary_key = "email_notification_settings_id"
belongs_to :building_user_pair
end
class EtlResult < ActiveRecord::Base
self.primary_key = 'etl_results_id'
# table has 5 rows, with values 1 -5 with the statuses "HUNKY DORY", "MO BETTA", "LIMPING ALONG", "DISMAIL FAILURE" and "UNEXPECTEDLY IDLE"
end
class EtlResultNotification < ActiveRecord::Base
belongs_to :building
belongs_to :user
end
class EtlStatus < ActiveRecord::Base
self.primary_key = 'etl_status_id'
belongs_to :building
has_many :users, :through => :email_notifications
end
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#buildings = Building.active
#bup = []
#email_notifications_settings = []
#buildings.each do |bldg|
bup = BuildingUserPair.where(user_id: params[:id], building_id: bldg.building_id).first_or_create
#email_notifications_settings[bldg.building_id] =
EmailNotificationSetting.where(building_user_pairs_id: bup.building_user_pairs_id).all
end
end
my users/show.html.erb contains this:
<%= form_for #user do |f| %>
<% #buildings.each do |bldg| %>
<div class="row">
<div class="col-md-4">
<%= bldg.name %>
</div>
<%= f.fields_for :email_notification_settings do |ens| %>
<%= ens.collection_check_boxes( #email_notifications_settings[bldg.building_id],
EtlResult.all, :etl_result_id, :result_name) %>
</div>
<% end %>
<% end %>
<div class="form-buttons">
<%= submit_tag("Update", data: { buildings: #buildings}) %>
</div>
The etl_result_notification table has just two columns, besides
it's primary key, a building-user-pair column and then a number,
1-5 that is a foreign key to the Etl Results table. Thus the idea is that a new line gets
created for a checkbox, and if a checkbox is newly unchecked, the row in the table is deleted.
Like I said, not even sure if form_for and collection_check_boxes was even designed to do this.
My problem is that the checkboxes are not being properly initialized. They all come up unchecked.
I'm guessing I need to pass other paraemters to collection_check_boxes, but I can't think
what they should be.
TIA
I think that you are over complicating your question,
what you want to do is save a list of id that march to the lest of emails the user want to get.
Rubyist posted https://stackoverflow.com/a/23340368/1380867 witch is a good example of what you want to do.
you should create a serialize :email_notification_ids
#MODEL
class User < ActiveRecord::Base
serialize :email_notification_ids
end
Hope this helps
Happy Codding

return the count for element in a has_many relationships

i have 2 models one is listing and user
user has_many listings
listing belongs_to user
i have a view setup , i want to display for each user their own listings count ,i try this code :
<% User.all.each do |user| %>
<%= user.listings.count %>
<% end %>
i want to grab the listing count for each user . i found a bunch of solution here , all return the loop .other solutions i tried is to create a class method .
def count_listings
Listing.where(:user_id => user.id).count
end
try to call this way <%= User.count_listings%> it doesn't work .
for some reason there something i'm missing ,can't quite figure it out .
The :counter_cache option can be used to make finding the number of belonging objects more efficient. Consider these models:
class Order < ActiveRecord::Base
belongs_to :customer
end
class Customer < ActiveRecord::Base
has_many :orders
end
With these declarations, asking for the value of #customer.orders.size requires making a call to the database to perform a COUNT(*) query. To avoid this call, you can add a counter cache to the belonging model:
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: true
end
class Customer < ActiveRecord::Base
has_many :orders
end
With this declaration, Rails will keep the cache value up to date, and then return that value in response to the size method.
Although the :counter_cache option is specified on the model that includes the belongs_to declaration, the actual column must be added to the associated model. In the case above, you would need to add a column named orders_count to the Customer model. You can override the default column name if you need to:
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: :count_of_orders
end
class Customer < ActiveRecord::Base
has_many :orders
end
Counter cache columns are added to the containing model's list of read-only attributes through attr_readonly.
source: Rails guide on associations
..scroll down to options of belongs_to
If all you need is what you show in the example you can do it better as follows
<% Listing.group(:user_id).count.each do |user, count| %>
<%= "user: #{user} has #{count} listings" %>
<% end %>
This does a single query to the database and fetches only what you need.
SELECT COUNT(*) AS count_all, user_id AS user_id FROM `listings` GROUP BY user_id
and returns a hash like:
{
1: 123,
2: 231
}
#{ user_id: count }

Resources