Why am I seeing a hash rendered to the DOM? - ruby-on-rails

I am building a Portfolio website that has a simple view file for projects I have worked on. On my "work" view I render a collection of "technologies" that I used to build a particular product. This is working great, and each technology renders just fine. However, right below the that renders the technologies the plain hash is also being rendered, and I cannot figure out why.
I am following a tutorial for this and I have double checked that my code is the same as the instructor's.
Work View
<%= image_tag #work_item.main_image unless #work_item.main_image.nil? %>
<h1>Title: <%= #work_item.title %></h1>
<em><%= #work_item.subtitle %></em>
<p><%= #work_item.body %></p>
<h2>Technologies Used:</h2>
<%= #work_item.technologies.each do |t| %>
<p><%= t.name %></p>
<% end %>
Schema
create_table "technologies", force: :cascade do |t|
t.string "name"
t.bigint "work_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["work_id"], name: "index_technologies_on_work_id"
end
controller method being used via def show via a before_action
def set_work
#work_item = Work.find(params[:id])
end
Technology Model
class Technology < ApplicationRecord
belongs_to :work
end
Work Model
class Work < ApplicationRecord
has_many :technologies
accepts_nested_attributes_for :technologies,
reject_if: lambda { |attrs| attrs['name'].blank? }
include Placeholder
validates_presence_of :title, :body, :main_image, :thumb_image
def self.react
where(subtitle: "React")
end
scope :ruby_on_rails, -> { where(subtitle: "Ruby on Rails") }
after_initialize :set_defaults
def set_defaults
self.main_image ||= Placeholder.image_generator(height: 600, width: 400)
self.thumb_image ||= Placeholder.image_generator(height: 350, width: 200)
end
end
Here is a screenshot of what I'm seeing
https://imgur.com/a/2T3SRZv

Because the = in
<%= #work_item.technologies.each do |t| %>
indicates that you want #work_item.technologies to be output to the view.
Instead, use
<% #work_item.technologies.each do |t| %>
BTW, that's not a hash, it's an enumerable.

Related

Unpermitted parameters In Rails 5 Project

I've spent a few hours trying to solve this problem in a Rails 5 project that I have. The issue is that I keep getting:
Unpermitted parameters: :item_instance_ids, :note_ids
when I submit a form. I believe that the relationships between the models are wrong. I'm using a polymorphic relationship which is the first time I've used it. I've looked through so many posts on StackOverFlow as well as guides on the web but nothing seems to help me.
Basically, I have an incoming purchases form - like an ordering form and within that form you should be able to add multiple items, like a laptop, keyboard, monitor, to the order => the item instances model.
Anyways, here is my code:
incoming_purchases.rb:
class IncomingPurchase < ApplicationRecord
# Relations
has_many :item_instance, :as => :instance_wrapper
has_many :notes, :as => :notable
belongs_to :user
end
item_instance.rb
class ItemInstance < ApplicationRecord
# Relations
belongs_to :instance_wrapper, polymorphic: true
belongs_to :item
belongs_to :user
has_many :notes, :as => :notable
end
views/incoming_purchases/_form.html.erb:
<%= simple_form_for(#incoming_purchase) do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<div class="form-inputs">
<%= f.association :item_instance, as: :check_boxes, :label_method => lambda { |item_instance| "#{item_instance.item.description}" } %>
<%= f.label(:date_ordered, "Order Date:") %>
<%= f.text_field(:date_ordered, class: 'form-control-date') %>
<%= f.association :user, :label_method => lambda { |user| "#{user.username}" } %>
<%= f.input :order_number %>
<%= f.input :vendor %>
<%= f.input :po_number %>
<%= f.input :tax %>
<%= f.input :shipping %>
<%= f.association :notes %>
</div>
<div class="form-actions">
<%= f.button :submit, class: "btn btn-outline-success" %>
</div>
<% end %>
incoming_puchases_controller.rb:
class IncomingPurchasesController < ApplicationController
before_action :set_incoming_purchase, only: [:show, :edit, :update, :destroy]
def new
#incoming_purchase = IncomingPurchase.new
end
def create
puts '*********************'
puts params
puts '*********************'
puts incoming_purchase_params
puts '**********************'
#incoming_purchase = IncomingPurchase.new(incoming_purchase_params)
respond_to do |format|
if #incoming_purchase.save
format.html { redirect_to #incoming_purchase, notice: 'Incoming purchase was successfully created.' }
format.json { render :show, status: :created, location: #incoming_purchase }
else
format.html { render :new }
format.json { render json: #incoming_purchase.errors, status: :unprocessable_entity }
end
end
end
private
def set_incoming_purchase
#incoming_purchase = IncomingPurchase.find(params[:id])
end
def incoming_purchase_params
params.require(:incoming_purchase).permit(:item_instances_id, :date_ordered, :user_id, :order_number, :vendor, :po_number, :tax, :shipping, :notes_id)
end
end
schema.rb:
ActiveRecord::Schema.define(version: 2020_08_31_200026) do
create_table "incoming_purchases", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
t.bigint "item_instances_id"
t.date "date_ordered"
t.bigint "user_id"
t.string "order_number"
t.string "vendor"
t.integer "po_number"
t.decimal "tax", precision: 8, scale: 2
t.decimal "shipping", precision: 8, scale: 2
t.bigint "notes_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["item_instances_id"], name: "index_incoming_purchases_on_item_instances_id"
t.index ["notes_id"], name: "index_incoming_purchases_on_notes_id"
t.index ["user_id"], name: "index_incoming_purchases_on_user_id"
end
create_table "item_instances", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
t.integer "inv_number"
t.string "serial"
t.integer "po_number"
t.date "po_date"
t.date "invoice"
t.date "date_out"
t.decimal "cost", precision: 8, scale: 2
t.string "acro"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "item_id"
t.index ["item_id"], name: "fk_rails_6ea33fd9d0"
end
add_foreign_key "incoming_purchases", "item_instances", column: "item_instances_id"
end
Oh, on the controller I tried:
params.require(:incoming_purchase).permit({ :item_instance_ids => [] }, :date_ordered, :user_id, :order_number, :vendor, :po_number, :tax, :shipping, :notes_id)
Again, I think the problem is how the relationship is set up between these two models. Thank you for any help.
I tried changing my permit params to the following:
params.require(:incoming_purchase).permit(:item_instances_id, :date_ordered, :user_id, :order_number, :vendor, :po_number, :tax, :shipping, notes_id: [], item_instances_id: [])
I was able to add an item but of course item_instances_id did not go through. When the params comes through it looks like this:
{"utf8"=>"✓", "authenticity_token"=>"d3jF73WyKCs69RSCFDvQlh7RyUAg0GQk8m7GKHX6/tt+Ve/1Y1oE5P1UtIMJfCIYS+YL0DwZth9UlDcnyW1uiA==", "incoming_purchase"=>{"item_instance_ids"=>["", "31"], "date_ordered"=>"2020-09-01", "user_id"=>"2", "order_number"=>"1", "vendor"=>"1", "po_number"=>"1", "tax"=>"1", "shipping"=>"1", "note_ids"=>[""]}, "commit"=>"Create Incoming purchase", "controller"=>"incoming_purchases", "action"=>"create"}
notice the item_instance_ids however, on the incoming_purchases model it's
item_instances_id notice the position of that s on ids and instances.
It looks like the filters you are passing into permit are not correct.
It probably needs to be note_ids: [] as this is a has_many relationship.
And when passing nested parameters into permit they should be placed at the end. So, you also have to move item_instance_ids to the end, either before or after note_ids: [].
Edit
You might be better off with a has_many :though relationship for tying items to a purchase. I'm not sure how your Item model looks like so I kept it simple.
incoming_purchase.rb
class IncomingPurchase < ApplicationRecord
has_many :purchase_items
has_many :items, through: :purchase_items
end
purchase_item.rb
class PurchaseItem < ApplicationRecord
belongs_to :incoming_purchase
belongs_to :item
end
item.rb
class Item < ApplicationRecord
has_many :purchase_items
has_many :incoming_purchases, through: :purchase_items
end

Retrieving a piece of data from a different Active Record Model/table

Any advice would be most appreciated.
Is it possible to retrieve the email address used by a user, posting a comment, in a list#show/show.html.erb view? Without adding another column "email" to my Comments model?
The best I can do is retrieve the user_id, which is not that helpful.
<% #list.comments.each do |comment| %>
<p><%= comment.body%></p>
<p><%= comment.user_id %>
<% end %>
Comment.rb
class Comment < ApplicationRecord
belongs_to :list, optional: true
belongs_to :user
Comments Table
create_table "comments", force: :cascade do |t|
t.text "body"
t.bigint "list_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id"
t.index ["list_id"], name: "index_comments_on_list_id"
t.index ["user_id"], name: "index_comments_on_user_id"
end
List controller#show
def show
#list = List.find(params[:id])
#current_user = current_user.id
end
Use Module#delegate from ActiveSupport:
class Comment < ApplicationRecord
belongs_to :list, optional: true
belongs_to :user
delegate :email, to: :user
end
<% #list.comments.each do |comment| %>
<p><%= comment.body%></p>
<p><%= comment.user_id %>
<p><%= comment.email %>
<% end %>
And make sure you use .includes or .eager_load in the controller to avoid a N+1 query:
def show
#list = List.includes(comments: :user).find(params[:id])
#current_user = current_user.id
end

Rails Access data from a different Model within a Controller

UPDATE: Problem solved, thanks to Sebastian and Gabriel for the helpful pointers.
The relevant changes to my code are as follows:
app/controllers/pomodoro_cycles_controller.rb
def pomodoro_collections
{
pomodoro_collection_0: Pomodoro.offset(0).first(100),
pomodoro_collection_1: Pomodoro.offset(100).first(100)
}
end
app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p>
<%= pomodoro.id %>
<%= pomodoro.color %>
</p>
<% end %>
<% end %>
NOTA BENE:
The #first method in ActiveRecord returns an Array, so the keys in my original Hash were nested Arrays. Instead, the following was sufficient to return an Array of Pomodoro objects:
Pomodoro.offset(0).first(100)
DESCRIPTION OF ORIGINAL PROBLEM
Rails 5, PostgreSQL
PROBLEM: I cannot access Pomodoro.all from within PomodoroCycleController
I have two scaffolds: Pomodoro and PomodoroCycle, and I want to access the full list of Pomodoros within the PomdoroCycle controller.
The following code is kept simple, in order to make as clear as possible what I'm trying to do. If I can do these things, then I'll be able to do much more, but one step at a time.
Regarding the db migration files, I have already run bundle exec rails db:migrate
I want to display a full list of Pomodoros in the PomodoroCycle Show View (later to be displayed in Index), but I don't know what is missing.
From app/controllers/pomodoro_cycles_controller.rb
def show
#pomodoros_collections = pomodoro_collections
end
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
From app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |collection| %>
<p><%= collection %></p>
<% end %>
However, this displays nothing in the browser.
app/models/pomodoro_cycle.rb
class PomodoroCycle < ApplicationRecord
has_many :pomodoros
end
app/models/pomodoro.rb
class Pomodoro < ApplicationRecord
belongs_to :pomodoro_cycle
end
Updated db/migrate/20180103032759_create_pomodoro_cycles.rb:
class CreatePomodoroCycles < ActiveRecord::Migration[5.1]
def change
create_table :pomodoro_cycles do |t|
t.string :activity
t.integer :iteration
t.integer :matrix_side_length
t.datetime :created_at
t.datetime :completed_at
t.string :category_labels, array:true, default: []
t.string :category_colors, array:true, default: []
t.string :username
t.timestamps
end
create table :pomodoros do |t|
t.belongs_to :pomodoro_cycle, index: true
t.datetime :completed_at
t.timestamps
end
add_index :pomodoros, :pomodoro_cycle_id
end
end
Untouched db/migrate/20180103054425_create_pomodoros.rb
class CreatePomodoros < ActiveRecord::Migration[5.1]
def change
create_table :pomodoros do |t|
t.boolean :status
t.string :category
t.string :color
t.datetime :completed_at
t.string :username
t.timestamps
end
end
end
First of all, as #SebastianPalma pointed out in the comments, the syntax is wrong
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
should be:
def pomodoro_collections
{
pomodoro_collection_0: [Pomodoro.offset(0).first(100)],
pomodoro_collection_1: [Pomodoro.offset(100).first(100)]
}
end
Make the keys in the hash symbols
Then to display each Pomodoro put something like:
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p><%= pomodoro.id %></p> #Or the attribute you want to display
<% end %>
<% end %>
Hope this help

Importing CSV data into Rails app, using something other then the association "id"

I am trying to import a few CSV files into my rails app. I learned and managed to import tables into Models without association.
Now i have managed to import the data into a table that has associations, but only by entering the actual "id" number on the CSV column. Although functional, this isn't really an option because i have many tables with thousands of IDs.
My main goal is to be able to use the column in the CSV and type in the actual value (that exists in the other model it is associated with), instead of the id number.
I have a Country model and a Ports model. The Ports model is associated with country_id
Port Model
class Port < ApplicationRecord
def self.import(file)
#code
CSV.foreach(file.path, headers: true) do |row|
port = find_by_id(row["id"])
Port.create! row.to_hash
end
end
belongs_to :shipment_type
belongs_to :country
has_many :origins, :class_name => 'Rate'
has_many :destinations, :class_name => 'Rate'
end
Country Model
class Country < ApplicationRecord
def self.import(file)
#code
CSV.foreach(file.path, headers: true) do |row|
Country.create! row.to_hash
end
end
has_many :ports, dependent: :destroy
end
schema.db
create_table "ports", force: :cascade do |t|
t.string "name"
t.string "port_code"
t.integer "shipment_type_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "country_id"
t.index ["country_id"], name: "index_ports_on_country_id", using: :btree
t.index ["shipment_type_id"], name: "index_ports_on_shipment_type_id", using: :btree
end
create_table "countries", force: :cascade do |t|
t.string "name"
t.string "country_code"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shipment_types", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
The associations are working because i am able to manually add them view my forms i create just fine.
<%= form_for(port) do |f| %>
<% if port.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(port.errors.count, "error") %> prohibited this port from being saved:</h2>
<ul>
<% port.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :port_code %>
<%= f.text_field :port_code %>
</div>
<div class="field">
<%= f.label :shipment_type_id %>
<%= f.collection_select :shipment_type_id, ShipmentType.all, :id, :name %>
</div>
<div class="field">
<%= f.label :country_code %>
<%= f.collection_select :country_id, Country.all, :id, :country_code %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Any guidance or help would be greatly appreciated. I have been going in circles with this for days now.
ADDING SAMPLE TABLE FROM CSV FILE.
A shipment_type is a ruby object, you want to send a string.
If you are needing to import relationships, add methods on the Port model like so
class Port < ApplicationRecord
def shipment_type_name
shipment_type.try(:name)
end
def shipment_type_name=(name)
self.shipment_type = ShipmentType.where(:name => name).first_or_create
end
def country_country_code
country.try(:country_code)
end
def country_country_code=(code)
self.country = Country.where(:country_code => code).first
end
end
Then in the CSV you'd send a shipment_type_name and country_country_code attributes.
You would do something similar to other relationships.
You may want to use this gem for importing CSV:
https://github.com/michaelnera/active_record_importer
It's easy to use.
Thank you everyone for the help. Below is what ended up working for me. The biggest issue i was getting was Origin and Destination. There is only one Port table, which includes a list of the Ports. Ports are used for both Origin and Destination.
class Rate < ApplicationRecord
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
rate = find_by_id(row["id"])
Rate.create! row.to_hash
end
end
belongs_to :origin, :class_name => 'Port'
belongs_to :destination, :class_name => 'Port'
belongs_to :carrier
belongs_to :shipment_category
belongs_to :unit_of_measure
has_many :additional_items
# associatiing Origin and Destination Port Code
def origin_port_code
origin.try(:port_code)
end
def origin_port_code=(port_code)
self.origin = Port.where(:port_code => port_code).first
end
def destination_port_code
destination.try(:port_code)
end
def destination_port_code=(port_code)
self.destination = Port.where(:port_code => port_code).first
end
# associating carrier name
def carrier_name
carrier_name.try(:name)
#code
end
def carrier_name=(name)
self.carrier = Carrier.where(:name => name).first
#code
end
# associating Shipment Category Name
def shipment_category_name
shipment_category.try(:name)
end
def shipment_category_name=(name)
self.shipment_category = ShipmentCategory.where(:name => name).first
end
# associating unit_of_measure name
def unit_of_measure_name
unit_of_measure.try(:name)
#code
end
def unit_of_measure_name=(name)
self.unit_of_measure = UnitOfMeasure.where(:name => name).first
#code
end
end

accessing model that belongs_to another model

I have a model 'Galleries', which has the 'has_many' relationship with 'Exhbition Images' (and opposingly the belongs_to).
I want to display the images associated with each gallery via the Gallery model. However I am encountering the error
undefined method `exhibition_images' for #<Gallery::ActiveRecord_Relation:0x00000108159dd0>
when i try to implement this line of code
gallery index:
<%= #gallery.exhibition_images.each do |exhibition_image| %>
<%= exhibition_image.title %>
<% end %>
I am sure this is very simple and I am missing something obvious.
Galleries controller
def index
#gallery = Gallery.all
end
Gallery model
class Gallery < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: :slugged
belongs_to :guide
has_many :exhibition_images
accepts_nested_attributes_for :exhibition_images
end
Exhibition Images
class ExhibitionImage < ActiveRecord::Base
belongs_to :gallery
has_attached_file :image, styles: { small: "100x100", guide: "500x500" }
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
end
SCHEMA
create_table "exhibition_images", force: true do |t|
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "gallery_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "galleries", force: true do |t|
t.string "title"
t.string "slug"
t.integer "guide_id"
t.datetime "created_at"
t.datetime "updated_at"
end
undefined method `exhibition_images' for #<Gallery::ActiveRecord_Relation:0x00000108159dd0>
You are trying to access exhibition_images on an ActiveRecord::Relation. You have a relation object because you're calling:
#gallery = Gallery.all
That gives you a scope that you can use to chain together more query conditions. You need to get an individual gallery object to be able to call exhibition_images. You can do this by calling first on #gallery, or you need to execute the query and iterate through the results:
<%= #gallery.each do |gallery| %>
gallery.exhibition_images.each do |exhibition_image| %>
<%= exhibition_image.title %>
<% end %>
<% end %>
If you do that, however, I'd be sure to eager load the exhibition_images:
#gallery = Gallery.includes(:exhibition_images).all
The problem is that #gallery contains an array of Gallery objects.
In your controller you should have:
#galleries = Gallery.all
And then iterate over the #galleries array
<% for gallery in #galleries %>
<%= gallery.exhibition_images.each do |exhibition_image| %>
<%= exhibition_image.title %>
<% end %>
<% end %>

Resources