Rails list model attribute - ruby-on-rails

I have an events model that has an attribute called location that is user defined. I want to create a list of those locations sorted by their counts. How do I grab all the values of an attribute?
**EDIT**
Events Controller
def index
#tags = Event.tag_counts.order('count DESC').limit(12)
//code
end
How I listed my other attribute tags w/ acts_as_taggable
<div class="sbody sbody-2">
<ul>
<% #tags.each do |tag| %>
<li>
<%= link_to(:controller => "events", :action => "index", :search => tag.name) do %>
<i class="icon-tag icon-white"></i> <%= tag.name.titleize %>
<% end %>
</li>
<% end %>
</ul>
</div>
Event Model
class Event < ActiveRecord::Base
belongs_to :member
attr_accessible :blurb, :details, :category, :tags, :video, :website, :name, :avatar, :banner, :tag_list, :location, :address,
:city, :zipcode, :state, :country, :start_date, :end_date, :start_time, :end_time
validates :location, presence: true,
length: {
maximum: 40,
message: 'must not be more than 40 characters.',
minimum: 2,
message: 'must be longer than 2 characters.'
}
end
Schema
class CreateEvents < ActiveRecord::Migration
def change
create_table :events do |t|
t.references :member
t.text :category
t.text :tags
t.text :website
t.text :video
t.text :details
t.text :blurb
t.text :name
t.timestamps
end
add_index :events, :member_id
add_attachment :events, :banner
add_attachment :events, :avatar
end
end

locations_count = Hash.new{0} // Create Hash to store location counts (initialize counts to 0)
Event.all.each { |event| locations_count[event.location] += 1 } // Iterate through each Event and increment the count for its location
locations_count.sort_by { |key, value| value } // Sort location counts by the value

Related

Nested forms, using form_with and fields_for, iteration not working

This is driving me crazy. Probably I'm overlooking something obvious, but I've read all other posts on the same topic and can't solve the problem. Thanks in advance for any help.
I have a model Klasse and a Model Klp. An instance of klasse has many klps. I would like to create klp in the same form as klasse. I've set this up using form_with and fields_for, and in klasses_controller.rb I have 3.times { #klasse.klps.build } But this iteration does not seem to work. The fields_for block is only shown once instead of 3 times.
The form in new.html was created following the rails "getting started" guide, and the fields_for block following the "form helpers" guide.
Here is my code:
app\models\klasse.rb:
class Klasse < ApplicationRecord
has_many :klps
has_many :people, :through => :klps
accepts_nested_attributes_for :klps, allow_destroy: true, :reject_if => :all_blank
validates :name, presence: true,
length: { minimum: 1 },
uniqueness: true
validates :klp_std_soll, presence: true,
length: { minimum: 1 }
def klp_std_ist
self.klps.sum("std")
end
end
app\models\person.rb
class Person < ApplicationRecord
has_many :klps
has_many :klasses, :through => :klps
validates :name, presence: true,
length: { minimum: 1 },
uniqueness: true
validates :vorname, presence: true,
length: { minimum: 1 }
def last_and_first_name
"#{name}, #{vorname}"
end
end
app\models\klp.rb
class Klp < ApplicationRecord
belongs_to :klasse
belongs_to :person
end
app\controllers\klasses_controller.rb
class KlassesController < ApplicationController
def new
#klasse = Klasse.new
3.times { #klasse.klps.build }
end
private
def klasse_params
params.require(:klasse).permit(:name, :klassentyp_id, :klp_std_soll, klps_attributes: [:id, :person_id, :std, :_destroy])
end
end
app\controllers\klps_controller.rb
class KlpsController < ApplicationController
def new
#klp = Klp.new
end
private
def klp_params
params.require(:klp).permit(:klasse_id, :person_id, :std)
end
end
app\views\klasses\new.html.erb
<%= form_with scope: :klasse, url: klasses_path, local: true do |form| %>
<ul>
<%= form.fields_for :klps do |klps_form| %>
<p>
<%= klps_form.label :person %>
<%= klps_form.collection_select :person_id, Person.all, :id, :last_and_first_name %>
<%= klps_form.label :std %>
<%= klps_form.text_field :std %>
</p>
<% end %>
</ul>
<p>
<%= form.submit %>
</p>
<% end %>
Try setting your collection of klps to an instance variable that you can then pass as an explicit "record object" argument to the fields_for method:
apps/controllers/klasses_controller.rb
def new
#klasse = Klasse.new
#klps = 3.times { #klasse.klps.build }
end
app\views\klasses\new.html.erb
<%= form.fields_for :klps, #klps do |klps_form| %>
... your form...
<% end %>
See also: How do I pass an array to fields_for in Rails?
Reference: https://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
I am giving an example see
customer.rb
class Customer < ApplicationRecord
has_many :addresses, dependent: :destroy
accepts_nested_attributes_for :addresses, allow_destroy: true
end
address.rb
class Address < ApplicationRecord
belongs_to :customer
end
customers_controller.rb
class CustomersController < ApplicationController
def new
#customer = Customer.new
3.times {
#customer.addresses.build //You can use #customer.addresses.new
}
end
end
def customer_params
params.require(:customer).permit(:id, :name, addresses_attributes:[:id,:country, :state])
end
new.html.haml
= simple_form_for #customer do |f|
= f.input :name, label: false, wrapper: false, input_html: { class: "gui-input"}, placeholder: "Customer Name"
= f.fields_for :addresses do |a|
= render 'addresses/fields', a: a
view/addresses/_fields.html.haml
= a.input :country, as: :select, label:false, :wrapper => false, prompt: "Select country"
= a.input :city, label:false, :wrapper => false, input_html: {class: "gui-input", maxlength: 80, minlength: 2}, placeholder: "City"
for more info
https://www.pluralsight.com/guides/ruby-ruby-on-rails/ruby-on-rails-nested-attributes
use
<%= form.fields_for :klps_attributes do |klps_form| %>

rendering existing categories in view, rails

How do I render a list of existing categories in my views?
My categories table has a :name column.
products view
= f.select :category_name, Category.all.map{|s| [s.name]}
Category.rb
class Category < ActiveRecord::Base
has_many :categoricals
validates :name, uniqueness: { case_sensitive: false }, presence: true
acts_as_tree order: "name"
end
Product.rb
class Product < ActiveRecord::Base
include ActionView::Helpers
include Categorizable
end
UPDATE:
Full product form
= simple_form_for #product, html: { multipart: true } do |f|
= f.input :title, placeholder: "Product Name", required: false, label: false
= f.input :description, placeholder: "Description", required: false, label: false
= f.select :category_name, Category.all.map{|s| s.name}
= f.input :image, label: false
= f.button :submit, class: "button"
UPDATE #2:
products controller
def update
if #product.update(product_params)
redirect_to #product, notice: "Your Product was successfully updated!"
else
render 'edit'
end
end
categorical model
class Categorical < ActiveRecord::Base
belongs_to :category
belongs_to :categorizable, polymorphic: true
validates_presence_of :category, :categorizable
end
categorizable module
module Categorizable
extend ActiveSupport::Concern
included do
has_many :categoricals, as: :categorizable
has_many :categories, through: :categoricals
end
def add_to_category(category)
self.categoricals.create(category: category)
end
def remove_from_category(category)
self.categoricals.find_by(category: category).maybe.destroy
end
module ClassMethods
end
end
I never understand why people don't use collection_select over select:
= f.collection_select :category_id, Category.all, :id, :name
It seems the real problem is due to your relations:
#app/models/category.rb
class Category < ActiveRecord::Base
has_many :products
end
#app/models/product.rb
class Product < ActiveRecord::Base
belongs_to :category #-> requires category_id column in products table
end
This will allow you to reference the :category_id as follows:
= simple_form_for #product, html: { multipart: true } do |f|
= f.input :title, placeholder: "Product Name", required: false, label: false
= f.input :description, placeholder: "Description", required: false, label: false
= f.collection_select :category_id, Category.all, :id, :name
= f.input :image, label: false
= f.button :submit, class: "button"
Try:
= f.select :category_name, Category.all.map{|s| s.name}
In order to make this work, you have to have category_name column present in your products table.

Error: undefined method `each' for "1":String

I'm trying to associate a model Thing with another Thing on my things/new form. Each Thing has_many :things through a join table :related_things.
When I submit the form, I get this error:
NoMethodError in ThingsController#create
undefined method `each' for "1":String
Where did I go wrong with my code?
Thing model: I put asterisks around the line with the error message.
class Thing < ActiveRecord::Base
has_many :related_things
has_many :things, :through => :related_things
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "30x30!" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
def related_things
related_thing_ids = RelatedThing.
where("thing_a_id = ? OR thing_b_id = ?", self.id, self.id).
map { |r| [r.thing_a_id, r.thing_b_id] }.
flatten - [self.id]
Thing.where(id: related_thing_ids)
end
def related_thing_ids=(ids)
***ids.each do |id|***
record = RelatedThing.where(thing_a_id: self.id, thing_b_id: id).first
record ||= RelatedThing.where(thing_a_id: id, thing_b_id: self.id).first
record ||= RelatedThing.create!(thing_a_id: self.id, thing_b_id: id)
end
end
end
RelatedThing model:
class RelatedThing < ActiveRecord::Base
has_many :things
end
Things controller:
class ThingsController < ApplicationController
def show
#thing = Thing.find(params[:id])
#related_thing = RelatedThing.all
#thing.things.build
end
def new
#thing = Thing.new
#things = Thing.all
end
def create
#thing = Thing.new(thing_params)
if #thing.save
redirect_to #thing
else
render 'new'
end
end
private
def thing_params
params.require(:thing).permit(:name, :avatar, :related_thing_ids)
end
end
Things/new.html.erb:
<h1>Add Something!</h1>
<p>
<%= form_for #thing, :url => things_path, :html => { :multipart => true } do |f| %>
<%= f.text_field :name, :placeholder => "Name of the thing" %>
<br>
<%= f.label :related_things %>
<%= f.collection_select :related_thing_ids, Thing.all, :id, :name %>
<br>
<%= f.label :display_picture %>
<%= f.file_field :avatar %>
<br>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</p>
Schema.rb:
ActiveRecord::Schema.define(version: 20141016190146) do
create_table "related_things", force: true do |t|
t.integer "thing_a_id"
t.integer "thing_b_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "things", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
end
end
I'm using Rails 4.0.10.
Try Array(ids).each ..., which convert any object in an array and respond to :each
~ (main) > Array(nil)
=> []
~ (main) > Array([])
=> []
~ (main) > Array('')
=> [""]
~ (main) > Array(1)
=> [1]
See Kernel#Array

Index Sorted by Most Views

I'm having trouble with sorting an index of "pins" by most views. The view isn't showing any of the sorted "pins." When a user visits a pin, it stores the data in the 'visit_details' and 'visits' tables. In the model I have the functions to handle the sorting but I'm not sure if that is the problem or what's going on.
create_table "visit_details", force: true do |t|
t.integer "visit_id"
t.string "ip_address", limit: 15
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "visit_details", ["ip_address"], name: "index_visit_details_on_ip_address"
create_table "visits", force: true do |t|
t.integer "total_visits"
t.integer "unique_visits"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "pin_id"
end
add_index "visits", ["pin_id"], name: "index_visits_on_pin_id"
My controller:
class PinMostViewsController < ApplicationController
def index
#random_model = Pin.order('random()').first
#pins = Pin.top_viewed(params[:page], params[:date])
end
def pin_most_views_params
params.require(:pin).permit(:ip_address, :visit_id)
end
end
My pin.rb model stores the logic:
class Pin < ActiveRecord::Base
belongs_to :user
acts_as_commentable
has_attached_file :image, :styles => { :medium => "300X300>", :thumb => "100X100>" }
validates :image, presence: true
validates :description, presence: true
validates_attachment :image, content_type: { content_type: ["image/jpg", "image/jpeg", "image/png", "image/gif"] }
has_one :visit
validates :team, presence: true
validates :position, presence: true
def self.top_viewed(page, time_period)
case time_period
when "all_time"
Pin.all_time(page)
when "year"
Pin.order_most_viewed(page, 1.year.ago)
when "month"
Pin.order_most_viewed(page, 1.month.ago)
when "week"
Pin.order_most_viewed(page, 1.week.ago)
when "day"
Pin.order_most_viewed(page, 1.day.ago)
else
Pin.all_time(page)
end
end
def self.order_most_viewed(page, date)
visits, ids = {}, []
# Create a hash containing pins and their respective visit numbers
VisitDetail.includes(:visit).where('created_at >= ?', date).each do |visit_detail|
pin_id = visit_detail.visit.pin_id.to_s
if visits.has_key?(pin_id)
visits[pin_id] += 1
else
visits[pin_id] = 1
end
end
if visits.blank?
# Since no visits existed for this time period, we simply return an empty array
# which will display no results on the view page
[]
else
# Now we sort the pins from most views to least views
visits.sort_by{ |k,v| v }.reverse.each { |k, v| ids << k }
# With our array of ids, we get all of the pins in the particular order
Pin.page(page).per_page(30).where(id: ids).order_by_ids(ids)
end
end
# A nice query method that will sort by ids, used for the order_most_viewed class method above
def self.order_by_ids(ids)
order_by = ["case"]
ids.each_with_index.map do |id, index|
order_by << "WHEN id='#{id}' THEN #{index}"
end
order_by << "end"
order(order_by.join(" "))
end
def self.all_time(page)
Pin.includes(:visit)
.where('visits.id IS NOT NULL')
.order("visits.total_visits DESC")
.order("visits.total_visits > 0")
.page(page).per_page(30)
end
# Instance Methods
# ================
def image_remote_url=(url_value)
self.image = URI.parse(url_value) unless url_value.blank?
super
end
def previous
self.class.first(:conditions => ["id < ?", id], :order => "id desc")
end
def next
self.class.first(:conditions => ["id > ?", id], :order => "id asc")
end
end
And finally, my view (index of pins that are supposed to be sorted by most views)
<div id="pins" class="transitions-enabled">
<% #pins.each do |pin| %>
<div class="box panel panel-default">
<%= link_to image_tag(pin.image.url(:medium)), pin %>
<div class="panel-body">
<p><%= pin.description %></p>
<p><strong><%= pin.user.name if pin.user %></strong></p>
<% if current_user && pin.user == current_user %>
<div class="actions">
<%= link_to edit_pin_path(pin) do %>
<span class="glyphicon glyphicon-edit"></span>
Edit
<% end %>
<%= link_to pin, method: :delete, data: { confirm: 'Are you sure?' } do %>
<span class="glyphicon glyphicon-trash"></span>
Delete
<% end %>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
Sorry, I can't comment yet. Did try tracing it?
You can use p right in the html to see what's going on there.
Also you can use logger.info in controller side.
Thus you will see, where you problem is hiding. When you see nil or []

Accessing nested child attributes in parent model?

I have the following form for release, with fields for tracks being accepted as nested attributes within my release model.
<%= form_for(#release) do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id, :class => "text" %>
<%= f.text_field :title, :class => "text" %>
<%= f.fields_for :tracks do |builder| %>
<%= render 'track_fields', :f => builder %>
<% end %>
<% end %>
My release model contains:
accepts_nested_attributes_for :tracks, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => :true
accepts_nested_attributes_for :releases_tracks
before_save :order_tracks
before_update :order_tracks
def order_tracks
releases_tracks.each { |t| t.position = track_attributes.position }
tracks.each { |t| t.user_id = user_id}
tracks.each { |t| t.label_id = label_id}
end
def track_attributes=(track_attributes)
track_attributes.each do |attributes|
tracks.build(attributes)
artists_tracks.build(attributes)
end
end
Everything works well, except the line below where i'm trying to take the position value entered in the fields_for part of the form. I can access values from the parent form, user_id for example, but how do I access the child values?
releases_tracks.each { |t| t.position = track_attributes.position }
Thanks all!
(Note: I don't want to use acts_as_list for this)
try to use:
releases_tracks.each { |t| t.position = track_attributes[:position] } or
releases_tracks.each { |t| t.position = track_attributes["position"] }

Resources