Return Attribute of Class Instance - ruby-on-rails

Basically I have a table output on a page, base view. The headers (milestones) are dynamically integrated by passing the Milestones class and iterating through each with an .each do. The table rows are then initially generated by doing the same thing, pulling in client, trade, and units, into the first three columns. The rest of the columns should be dates (class entry attribute due) that are calculated by a lookup of sorts using the client and trade attributes accessible by the loop, and the milestone ID accessible by an array created when dynamically generating them for the headers. This all works correctly as it should, and the table generates fine.. however the dates are output as #<Entry:0x000000056faa90> and so on.
Now I have come across this problem before, and basically the fix was to add a class definition and tell it to return what I assume to be the attribute of that instance. For example:
class Client < ActiveRecord::Base
validates :id, presence:true
validates :client, presence:true
def name
return self.client
end
end
And this works great, but only for how I access the client. Unfortunately I am accessing entry.due differently. Here are my relevant bits of code.
base_controller.rb
class BaseController < ApplicationController
def index
#trades = Trade.all
#milestones = Milestone.all
##entries = Entry.all #Didn't seem relevant to how I am trying to access the information.
end
end
entry.rb
class Entry < ActiveRecord::Base
belongs_to :client
belongs_to :trade
belongs_to :milestone
validates :client, presence:true
validates :trade, presence:true
validates :milestone, presence:true
validates :due, presence:true
# Some of my trial and error; all to no avail.
def due
return self.due
end
def self.due
return self.due
end
def pickDue(c,t,m)
ret = self.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", c, t, m).first
return ret
end
end
base > index.html.erb
<table>
<thead>
<tr class="sort">
<th>Client</th>
<th>Trade</th>
<th>Units</th>
<%
ms = []
#milestones.each do |milestone|
ms.push(milestone)
%>
<th><%= milestone.name %></th>
<% end %>
</tr>
</thead>
<tbody>
<% #trades.order("entered").last(10).each do |trade| %>
<tr>
<td><%= trade.client.name %></td>
<td><%= trade.name %></td>
<td><%= trade.units %></td>
<% ms.each do |msr| %>
<td>
<%= Entry.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).first %>
<%#= Entry.pickDue(trade.client, trade, msr) %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
With this approach, I can load the page error-free, but instead of showing dates, I am shown objects like #<Entry:0x000000056faa90>.
If I add .due to the end of the selector:
<%= Entry.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).first.due %>
# undefined method `due' for nil:NilClass
If I add .due after anything else:
<%= Entry.due.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).first %>
<%= Entry.select("date_format(due, '%m/%d/%Y')").due.where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).first %>
<%= Entry.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).due.first %>
# stack level too deep
I have tried dozens of different methods acquired through several days of looking into this with no luck. Besides the current situation where it outputs objects instead of the object attribute, the next closest I've come I think is when I got an error saying it could not find the due method for an ActiveRecord object, but I don't remember how got to that error.
I would really appreciate any input.

If I understand correctly, I would get rid of that trial and error code in your model, and then in your view you can just call this:
Entry.where(client: trade.client, milestone: msr, trade: trade).pluck(:due)
This goes inside your milestone loop in your view, like this:
<% ms.each do |msr| %>
<td>
<%= Entry.where(client: trade.client, milestone: msr, trade: trade).pluck(:due).first %>
</td>
<% end %>
Pluck returns an array of values from the columns you specify (see the relevant section of the RailsGuide, so note that the .first method is the Array#first method, not the ActiveQuery#first method. You could even make a query that passes in all of the milestones at once instead of running a query for each milestone, thereby preventing an N+1 query situation.
That would be:
<% Entry.where(client: trade.client, trade: trade, milestone: ms).pluck(:due).each do |entry_due_date| %>
<td>
<%= entry_due_date %>
</td>
<% end %>
Lastly, while I gave you the code for the SQL query in the view, it is generally considered a bad practice in Rails to write SQL queries in your view, and you should probably make this into a method in your Entry model.

I think you might be looking for attributes method.
entry = Entry.new
entry.attributes #=> returns a hash of key-value attribute pairs

Related

Filter with another table's attribute (one to many) in Rails

Filter with another table's attribute (one to many) in Rails
I'm a beginner on rails and currently working on my class project. I have a lead and a lead_comments table, lead has_many lead_comments and lead_comment belong_to lead, which I got those established.
In Model:
class Lead < ApplicationRecord
has_many :lead_comments
end
class LeadComment < ApplicationRecord
belongs_to :lead
end
In my lead's index view, I am trying to setup a date filter base on the Update_at in the lead_comments, which is the attribute from another table:
<div class="date-search">
<%= form_tag leads_path, method: :get do %>
<%= date_field_tag 'date_search[date_from]', #date_search.date_from %>
<%= date_field_tag 'date_search[date_to]', #date_search.date_to %>
<%= submit_tag 'Date Search' %>
<% end %>
</div>
<table>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Phone Number</th>
<th>Last Updated Date</th>
<th colspan="3">Action</th>
</tr>
</thead>
<tbody>
<% #leads.each do |lead| %>
<tr>
<td><%= lead.lead_firstname %></td>
<td><%= lead.lead_lastname %></td>
<td><%= lead.lead_phone %></td>
<td><%= lead.lead_comments.maximum(:updated_at) %></td>
<td><%= link_to 'Detail', lead %></td>
<td><%= link_to 'Edit/Update', edit_lead_path(lead) %></td>
<td><%= link_to 'Remove', lead, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
In my lead controller, I set up another controller for lead search purpose:
def index
#date_search = LeadSearch.new(params[:date_search])
#leads = #date_search.scope
end
In my lead search model, I believe where my problem is:
class LeadSearch
attr_reader :date_from, :date_to
def initialize(params)
params ||= {}
#date_from = parsed_date(params[:date_from], 30.days.ago.to_date.to_s)
#date_to = parsed_date(params[:date_to], Date.today.to_s)
end
def scope
Lead.lead_comments.maximum(:updated_at).where('updated_at BETWEEN ? AND ?', #date_from, #date_to)
end
private
def parsed_date(date_string, default)
Date.parse(date_string)
rescue ArgumentError, TypeError
default
end
end
My lead_comment table only has one attributes, which is comment, along with the two automated date attributes created by Rails, created_at and updated_at.
Whenever I run the application I get an error that tells me undefined method "lead_comments". I thought I have already established the has_many and belong_to relationship and this still not working. Can someone please help me out and guide me to the right direction? Thank you for your time.
you are doing Lead.lead_comments which is wrong. You are using the class name instead of the object. There should be a Lead model's object.
#lead.lead_comments
First, try your query in rails console. then use it in the controller for better perspective. try following,
Lead.lead_comments # will give error
instead use,
Lead.last.lead_comments # success
Issues in your code:
Lead.lead_comments
Let's simplify this: think about lead_comments as a method that given a lead returns its comments. But we need a lead. That's why called on Lead does not work. That would work called on a Lead object.
Lead.lead_comments.maximum(:updated_at)
Here you're composing a SQL query though ActiveRecord but calling maximum stop this composition because that require a value so you're not able to call anything else after methods like maximum
Let's make your code work
def scope
Lead.where('updated_at BETWEEN ? AND ?', #date_from, #date_to)
end
in your view
<td><%= lead.lead_comments.maximum(:updated_at) %></td>
Note here that this code has always been correct because you call lead_comments on a lead object.
I know, this performs one query for each Lead object but to make it works with just one query looks like another question

ruby on rails Insert Multiple record in a table using form with no relationship

I am building a timesheet project. i want to insert multiple records depending on the count of projects allocated to employes.
the problem is how to insert multiple records into in a table using forms
For example is the image i was trying to do:
code for form is
<%= form_for(:timesheets, :url => {:action => 'create',:employee_id => #pro.id}) do |d| %>
<% if !#project.nil? %>
<% #project.each do |page| %>
<tr>
<%= d.hidden_field("employee_id" ,:value => #pro.id) %>
<%= d.hidden_field("project_id" ,:value => page.id) %>
<% if !page.employee_id.blank? %>
<td><%= page.prog_name %></td>
<td><%= d.text_field("IN",:class => "qty1") %></td>
<td><%= d.text_field("comments") %></td>
<% end %>
</tr>
<% end %>
<% end %>
<tr>
<td>Total hours</td>
<td colspan="2"><%= text_field_tag("total")%></td>
</tr>
<tr border="0">
<td><%= submit_tag("Submit") %></td>
<td colspan="2" border="0"></td>
</tr>
<% end %>
Your question is really vague, but I think I get what you're asking. I had to build a similar form recently, and used a form object with a row class, with an instance of the row class being initialized for every record that needed to be added.
See this for an introduction to form objects if you're not familiar already: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
class TimesheetForm
# include ActiveModel stuff here
# attr_reader :projects, ...
# validations for form as a whole, if desired
def initalize(employee)
#employee = employee
#projects = employee.projects
end
def submit(params)
# extract params and create new instances of TimesheetForProject for each set of timesheet params
# run validations on all #timesheet_rows
# persist if all validations pass
end
class TimesheetForProject
# validations for each timesheet record
def initialize(project)
#project = project
end
# timesheet-specific form logic
end
end
This doesn't contain a lot of details you need, but it's a rough conceptual outline of a pattern I've seen and used to solve this type of problem.

Rails printing the value of a database column

I'm new to rails and I have been experimenting. I have to tables companies and contacts and i have set up the associations and relationship. what I would like to do is this.
Currently, in the contacts table it brings back a number for the value of the company name. What I would like instead is the the value of the company name instead of the number. I have tried changing it from the company_id to company_name - but I then get an error about no method being found. Can anyone help on how I do this.
You can delegate to the company
class Contact < ActiveRecord::Base
delegate :name, to: :company, prefix: true
end
<%= #contact.company_name %>
I assume a contact will always belong to a company, if not add, allow_nil: true
If you're looping through them like this:
<% #contacts.each do |contact| %>
<tr>
<td><%= #contact.company_name %></td>
</tr>
<% end %>
I doubt you've defined #contact as you're looping through #contacts calling them contact you want
<% #contacts.each do |contact| %>
<tr>
<td><%= contact.company_name %></td>
</tr>
<% end %>
I.e. without the # - you're using a local variable not an instance variable.

How to display all has_many items in index view?

I have two models, a calculation name and a different table with a bunch of calculations that go along with it. I have successfully established a one to many relationship (one name has many states) so that when I display calculation variables in my name/show view it works perfectly:
names/show view
<p><%= #name.calc_name %></p>
<% #name.states.each do |state| %>
<p><%= state.orbital_subset %></p>
<% end %>
I would, however like to display variables in my state value on the index page of names. Currently, I have a nice list of each name but creating an inner loop that loops through the states doesn't seem to work well. This works great too:
names/index view
<% #names.each do |name| %>
<tr>
<td><%= name.calc_name %></td>
</tr>
<% end %>
The best solution I found online is of this format which does not work:
<% #names.states.each do |state| %>
<p><%= state.orbital_subset %></p>
<% end %>
Should I be nesting two do loops on the index page since I am looping both through all of the names and through all of the states? Here are my models:
class Name < ActiveRecord::Base
has_many :states
end
class State < ActiveRecord::Base
belongs_to :name
end
The error that I get when I view names index is:
NoMethodError in Names#index
<% #names.each do |name| %>
<% name.states.each do |state| %>
<td><%= state.orbital_subset %></td>
<% end %>
<% end %>

Nested forms in rails for existing objects

In my app, I have a page where I want admin users to be able to update a particular characteristic of my "Package" model, which belongs to both the "Order" model and the "Item" model. It's a little complicated, but I basically want to present in a table all of the Packages belonging to a given Item, ordered in a particular way (that's what my packages_for_log method below does), with two blanks for updating the weight of the item. All the updates should ideally be submitted at once, with a single submit button at the bottom of the page. I've attempted a whole bunch of solutions, and my current one is below, but gives this error when I visit the page in my server:
undefined method `actual_lbs' for #<ActionView::Helpers::FormBuilder:0x007ff67df6c9c8>
The error's confusing to me, cause I was hoping that I was calling that method on the package instance, not a helper. Bit confused. At any rate, my code is below. The relevant section of the view:
<% form_for(#item) do |a| %>
<% #item.packages_for_log.each do |p| %>
<%= a.fields_for p do |i| %>
<tr>
<td><%= p.order.name %></td>
<td><%= p.order.processed_notes %></td>
<% if p.order.user %>
<td><%= "#{p.order.user.name.first(3).upcase}-#{p.id}" %></td>
<% else %>
<td><%= p.order.id %></td>
<% end %>
<td>
<%= i.text_field :actual_lbs %>
</td>
<td>
<%= i.text_field :actual_oz %>
</td>
<%= i.hidden_field :true_weight, value: (i.actual_lbs + i.actual_oz/16) %>
</tr>
<% end %>
<% end %>
<% end %>
Relevant section of the package.rb model file:
attr_accessible :order_id, :price, :true_weight, :actual_lbs, :actual_oz
attr_accessor :actual_lbs, :actual_oz # These two are virtual attributes for the above calc
And I added resources :packages to my routes file.
Any idea what I'm doing wrong? It's important to me that I loop through to create a table based on "p" and then edit that same "p" object. Just not sure how to do it. Pretty new to Rails.
I think your problem is this line:
<%= i.hidden_field :true_weight, value: (i.actual_lbs + i.actual_oz/16)
You need to put p.actual_lbs and p.actual_oz
EDIT: By the way, you probably need to move the true weight calculation to your controller action (CREATE action). I don't think :true_weight will get passed as you intended it to, using the above method.

Resources