Error: undefined method `each' for "1":String - ruby-on-rails

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

Related

Rails create does not save params from checkbox

I am creating a website where a user (interviewer) can create a position (a job opening).
Even if the params are sent, my create action does not save them except for the current_user.
This is what I send:
positions_controller.rb
def new
#position = Position.new
end
def create
#position = Position.new(position_params)
#position.interviewer = current_interviewer
if #position.save
redirect_to position_path(#position)
flash[:success] = "You created a new position/opening"
else
render :new
end
raise
end
private
def set_position
#position = Position.find(params[:id])
end
def position_params
params.require(:position).permit(:title, :skills, :interviewer)
end
end
_form.html.erb
<%= simple_form_for [#interviewer, #position] do |f| %>
<%= f.input :title, required:true %>
<%= f.input :skills, as: :check_boxes, collection:[
['Python', "python"],
['Java', "java"],
['JavaScript', "javascript"],
['Ruby', "ruby"],
['C++', "c++"],
['Node.js', "node"],
['React', "react"],
['Django', "django"],
['Rails', "rails"],
['SQL', "sql"],
['Doker', "doker"],
['AWS', "aws"],
['Vue.js', "vue"],
['Marketing', "Marketing"],
['HR', "hr"],
['Finance', "finance"],
['IT', "it"],
], input_html: { multiple: true } %>
<%= f.submit "Submit position", class: "btn btn-primary" %>
<% end %>
position.rb
class Position < ApplicationRecord
validates :title, presence: true
belongs_to :interviewer
end
schema
create_table "positions", force: :cascade do |t|
t.string "title"
t.bigint "interviewer_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "candidate_id"
t.string "candidatures", default: [], array: true
t.string "skills"
t.index ["candidate_id"], name: "index_positions_on_candidate_id"
t.index ["interviewer_id"], name: "index_positions_on_interviewer_id"
end
My alternative was to replace the create code with:
#position = current_interviewer.positions.new(position_params)
but it still does not work.
Since you have a input_html: { multiple: true } for the params skills, you need to add the following in positions_controller.rb:
def position_params
params.require(:position).permit(:title, :interviewer, :candidate, skills:[])
end
Basically, your skills will be saved as an array if you allow input_html: { multiple: true } for a collection
In addition, you are not passing any params for candidate
In your table, you have interviewer_id but in your permitted params you have interviewer.
Change your params to permit interviewer_id instead.
Also, in your form you have <%= f.input :title, required:true %> Presence is required by default (at least with simple_form). You don't need to declare it in the form, but you should still keep it in your Model.

Rails 4 - Unpermitted parameters for nested param despite whitelisting

I have two models user_item and user_item_images.
schema.rb
create_table "user_item_images", force: :cascade do |t|
t.integer "user_item_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "picture"
end
create_table "user_items", force: :cascade do |t|
t.integer "user_id"
t.integer "item_id"
t.integer "status", default: 0
t.boolean "hide_banner", default: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "picture"
t.string "declined_reason"
end
I have a form where the user should submit a new user_item. The only field in the form is for pictures where the user can upload multiple pictures. On success, a single new user_item is created along with a new user_item_image for each picture that is uploaded.
form
<%= simple_form_for :user_item, url: user_items_path, html: {multipart: true} do |user_item_builder| %>
<%= user_item_builder.input :item_id, as: :hidden, input_html: { value: "#{#item.id}" } %>
<%= user_item_builder.simple_fields_for :user_item_images do |user_item_images_builder| %>
<%= user_item_images_builder.input :picture, as: :file, label: false, name: "user_item_images[picture][]", input_html: { multiple: true } %>
<% end %>
<% end %>
user_item_controller.rb
def create
#user_item = current_user.user_items.new(user_item_params)
raise 'foo'
if #user_item.save
params[:user_item][:user_item_images]['picture'].each do |a|
#user_item_image = #user_item.user_item_images.create!(:picture => a)
end
# Sends email to user when item request is created.
itemMailer.user_item_submission_email(current_user, #user_item.item).deliver_later
flash[:notice] = "Thank you for your item request!"
else
#user_item.errors.full_messages.each do |message|
flash[:alert] = message
end
end
redirect_to item_path(#user_item.item)
end
private
def user_item_params
params.require(:user_item).permit(:item_id, user_item_images_attributes: [:user_item_id, :picture])
end
user_item.rb
belongs_to :user
has_many :user_item_images
mount_uploader :picture, PictureUploader
accepts_nested_attributes_for :user_item_images
user_item_image.rb
mount_uploader :picture, PictureUploader
belongs_to :user_item
When I submit the form I get Unpermitted parameter: user_item_images in the console and I can't figure out why.
Change this:
<%= ..., name: "user_item_images[picture][]", ... %>
to this:
<%= ..., name: "user_item_images_attributes[picture][]", ... %>

5 form elements present, but only 4 are saving to the database. Rails

I am having a problem with my database. I am able to save all the elements of my form into the database but it is leaving out ":captcha" for some reason. :email, :first_name, :last_name and :user_message are all saving, but :captcha is not.
HTML form views/pages/index.html.erb
<%= form_for(#message) do |f| %>
<%= f.text_field :first_name, :class => "message_name_input message_input_default", :placeholder => " First Name" %>
<br><br>
<%= f.text_field :last_name, :class => "message_name_input message_input_default", :placeholder => " Last Name" %>
<br><br>
<%= f.text_field :email, :required => true, :class => "message_email_input message_input_default", :placeholder => " * Email" %>
<br><br>
<%= f.text_area :user_message, :required => true, :class => "message_user-message_input", :placeholder => " * Write a message" %><br><br>
<%= f.text_field :captcha, :required => true, :name => "captcha", :class => "message_input_default", :placeholder => " * #{#a} + #{#b} = ?" %><br><br>
<div id="RecaptchaField2"></div>
<%= f.submit "Send", :class => "messages_submit_button" %>
<% end %>
Pages Controller
class PagesController < ApplicationController
def index
#message = Message.new
#a = rand(9)
#b = rand(9)
session["sum"] = #a + #b
end
end
Messages Controller
class MessagesController < ApplicationController
def show
end
def new
#message = Message.new
end
def create
#message = Message.new(message_params)
if params["captcha"].to_i == session["sum"] && #message.save!
UserMailer.welcome_email(#message).deliver_now
redirect_to '/message_sent'
else
redirect_to '/'
end
end
private
def message_params
return params.require(:message).permit(:first_name, :last_name, :email, :user_message, :captcha)
end
end
Messages Migration
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.string :captcha
t.string :first_name
t.string :last_name
t.string :email
t.string :user_message
t.timestamps null: false
end
end
end
Schema
ActiveRecord::Schema.define(version: 20150822040444) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "messages", force: :cascade do |t|
t.string "captcha"
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "user_message"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Routes
Rails.application.routes.draw do
resources :pages
resources :messages
resources :admins
get '/' => 'pages#index'
get '/new' => 'messages#new'
post '/message_sent' => 'messages#create'
get '/message_sent' => 'messages#show'
end
EDITED Attempted this code, but instead of saving 4 elements, it executes the "else" statement and redirects as if it is not being saved at all.
Messages Controller
class MessagesController < ApplicationController
def show
end
def new
#message = Message.new
end
def create
#message = Message.new(message_params)
if params[:message][:captcha].to_i == session["sum"] && #message.save!
UserMailer.welcome_email(#message).deliver_now
redirect_to '/message_sent'
else
redirect_to '/'
end
end
private
def message_params
return params.require(:message).permit(:first_name, :last_name, :email, :user_message, :captcha)
end
end
Remove name attribute from here:
<%= f.text_field :captcha, :required => true, :name => "captcha", :class => "message_input_default", :placeholder => " * #{#a} + #{#b} = ?" %><br><br>
It happens because name parameter is generated by rails itself, and it's responsible to structure your query. Thus this erb line:
<%= f.text_field :first_name %>
Will generate this html:
<input type="text" name="message[first_name]">
And when you submit form it will produce query like this
{ message: { first_name: 'value_of_input' } }
But you provided custom name that overridden default behaviour and produces requests like this:
{ captcha: 'captcha_val', message: { first_name: 'some_val1', last_name: 'some_val2', ... } }
Then you extract message params from params:
def message_params
params.
require(:message).
permit(:first_name, :last_name, :email, :user_message, :captcha)
end
Finally you create message with this hash:
{ first_name: .., last_name: .., email: .., user_message: .. }

How to only show public posts and not show private ones in Ruby on Rails?

I have a collection (or post if you prefer) that a User creates. The collection can be public or private (default is true in database). I want to let the User see all of their collections, but want anyone to be able to see ONLY collections set to be public.
What I mean is that when anyone (User or not) goes to view the Collection (ex. myapp.com/collections/some-collection), if it is public it will show, but it it is private it will not show and maybe render a "This Collection is Private" and redirect. I could use some help, thanks!
db/schema.rb
create_table "collections", force: :cascade do |t|
t.string "title"
t.integer "user_id"
t.boolean "display", default: true
t.text "description"
end
app/views/collections/show.html.erb
<div class="row">
<div class="col-lg-10">
<h2><%= best_in_place #collection, :title, as: :input %></h2>
<p><%= best_in_place #collection, :description, as: :textarea %></p>
<%= simple_form_for #collection do |f| %>
<%= f.input :display, as: :boolean, checked_value: true, unchecked_value: false %>
<%= f.button :submit %>
<% end %>
</div>
</div>
app/controllers/collections_controller.rb
def show
#links = #collection.follows
#collection = Collection.find_by_id(params[:id]) if params[:id].present?
if params[:id].blank?
#collections = Collection.user_collections(params.merge({"user_id" => current_user.id}))
end
end
app/models/collection.rb
class Collection < ActiveRecord::Base
belongs_to :user
has_many :follows
def links_count
follows.count
end
def add_link(url)
status, options, link = Link.link_exist?(url)
unless status
options = Utils.parse_page(url)
link = Link.create(options)
link.download_image!
end
link.follow_link({:collection_id => self.id, :user_id => user_id})
end
def self.user_collections(options)
conds = []
conds << " user_id = #{options["user_id"]}" if options["user_id"].present?
conds = conds.blank? ? [] : conds.join(" AND ")
Collection.where(conds).order("updated_at DESC").page(options["page"]).per(16)
end
end
before_filter :is_public?, only: [ :action_name]
def is_public?
unless #collection.display?
redirect_to :back
end
end
#collections = Collection.where('display', true) This should return all collections that can be displayed.

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 []

Resources