undefined method `each' for #<String:0x0000559c6e193560> - ruby-on-rails

I'm trying to show the features of certain storage that the user selects from a form, but when trying to display the show view, I get
undefined method `each' for #<String:0x0000559c6e193560>
show.html.erb
<% #storage.features.each do |feature| %>
<div>
<%= feature %>
</div>
<% end %>
If I use <%= #storage.features %> the outcome is an array with the selected features, like this: ["Pet Free", "Smoke Detector", "Climate Controlled", "Easy Access"]. But I would like to show every feature separately, hence, I was trying to use .each
new.html.erb
<%= simple_form_for(Storage.new) do |f| %>
[...]
<% FeaturesData::Features.each do |feature| %>
<div>
<%= f.check_box :features, { multiple: true }, feature, nil %>
<%= f.label feature %>
</div>
<% end %>
<%= f.button :submit, 'Create Post'%>
<% end %>
app/models/concerns/features_data.rb
module FeaturesData
Features = [
"Pet Free",
"Security Camera",
"Smoke Detector",
"Climate Controlled",
"Locked Area",
"Easy Access",
"Independent Space"
]
end
storages_controller.rb
def storage_params
params.require(:storage).permit(
:title,
:description,
:address,
:storage_type,
:city,
:country,
:latitude,
:longitude,
:price,
:meters,
:user_id,
features: [],
photos: []
)
end

If running #storage.features.each returns undefined method 'each' for #<String:0x0000559c6e193560> error this means that #storage.features is a string, not an array. You are saying that calling <%= #storage.features %> returns an array but I suspect it's probably a string that looks like an array: '["Pet Free", "Smoke Detector", "Climate Controlled", "Easy Access"]'.
Have you perhaps forgotten to instruct Rails to serialize your features by adding serialize :features in your storage.rb model file?

Related

Nested form for array using upsert

Class
has_many :translators
accepts_nested_attributes_for :translators
Class Translator was created leveraging the postgresql array datatype via t.string :idiomas, array: true.
It belongs_to :user
Intent: use the declared locales as locales for which the user can create translations, using the Mobility gem in a pattern as such:
<% current_user.translator.idiomas.each do |idioma| %>
<%= Mobility.normalize_locale(idioma) %>: <%= form.text_field "title_#{Mobility.normalize_locale(idioma)}" %>
<% end %>
Setting the user.translator.idiomas
<%= form_with(url: set_translator_user_path(#user)) do |f| %>
<%= fields_for :translator do |ff| %>
<%= I18n.available_locales %>
<% I18n.available_locales.each do |locale| %>
<%= ff.checkbox :idiomas %> <%= locale %>
<% end %>
<% end %>
<% end %>
The users_controller defines user_params params.require(:user).permit({ translators: [idiomas: []] }, and would upsert given the idioma values
def set_translator
#user.translator.upsert(idiomas: params[:idiomas])
end
Right now, the array of application defined locales states [:en, :it, :sl, :de] , but the individual locale rendering is a string, not a symbol.
a) the upsert function does not run
NoMethodError (undefined method `upsert' for nil:NilClass
#user.translator.upsert(idiomas: params[:translator][:idiomas])
changing the controller action to
if #user.translator.nil?
#translator = Translator.create(user_id: #user.id, idiomas: params[:translator][:idiomas])
else
#translator = Translator.update(idiomas: params[:translator][:idiomas])
end
correctly creates and updates the record ( idiomas: ["en", "sl", "de"])
what is correct upsert syntax?

can't write unknown attribute `example` typed_store - Rails

I have this models where I wanna update my typestored columns using update_columns active record
I have searched numerous answers in stack, doesn't seems to have lot of resources regarding typestored.
show.html.erb
<%= form_tag print_booking_path(#booking), method: 'post' do %>
<%= label_tag :name %>
<%= text_field_tag :name, '', class: 'form-control' %>
<%= label_tag :age %>
<%= text_field_tag :age, '', class: 'form-control' %>
<%= submit_tag "Print", class: "btn btn-default" %>
<%= link_to 'Cancel', '#', class: 'btn btn-default', data: { dismiss: 'modal' } %>
<% end %>
bookings_controller
def print
#booking = Booking.find(params[:id])
if #booking.print_model(current_user, params[:name], params[:age])
render :print
else
render :print
end
end
booking model
def print_model(user, name_test, age_test)
self.update_columns(name: name_test, age: age_test)
end
typestore under booking model
typed_store :profile, coder: PostgresCoder do |i|
i.string :name, default: ""
i.integer :age, default: 0
end
the error appeared to be like this
can't write unknown attribute name
it's same like if I wanna to update like this self.increment!(:age)
I made a few tests here, and I don't know if it makes sense, but in case of updating stores, all the typestored columns are inside just one real column in the database (that are a normally in hash or Json).
So the straight foward way to do it is to wrap in brackets.
Try changing:
self.update_columns( name: name_test, age: age_test )
To:
self.update_columns( profile: { name: name_test, age: age_test} )

How to add address to users with Google map and jt-rails-address?

I'm new on rails. I work on a project, and I try to add physical address to my users. I want that the address can be help-complete with Google map for later exploitation. I find the jt-rails-address which look like perfect for my project. But I can't implement it. I need complete address (street, zip code, city & country).
add_address_to_users.rb :
class AddAddressToUsers < ActiveRecord::Migration[5.1]
def change
add_column :users, :address, :address
end
end
form edit.html.erb :
<div class="col-md-6 col-md-offset-3">
<%= form_for (#user), :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :phone, "Téléphone :" %>
<%= f.phone_field :phone, class: 'form-control' %>
<%= f.label :address, "Addresse :" %>
<div class="jt-address-autocomplete">
<!-- This field is used to search the address on Google Maps -->
<%= f.text_field :address, class: 'jt-address-search' %>
<!-- All fields are hidden because the javascript will set their value automatically -->
<% for attr in JT::Rails::Address.fields %>
<%= f.hidden_field "address_#{street}", class: "jt-address-field-#{street}" %>
<% end %>
<% for attr in JT::Rails::Address.fields %>
<%= f.hidden_field "address_#{zip_code}", class: "jt-address-field-#{zip_code}" %>
<% end %>
<% for attr in JT::Rails::Address.fields %>
<%= f.hidden_field "address_#{city}", class: "jt-address-field-#{city}" %>
<% end %>
<% for attr in JT::Rails::Address.fields %>
<%= f.hidden_field "address_#{country}", class: "jt-address-field-#{country}" %>
<% end %>
</div>
<%= f.submit "Enregistrer les changements", class: "btn btn-primary" %>
<% end %>
</div>
application.js :
// This function is call when Google Maps is loaded
window.googleMapInitialize = function(){
// Simple usage
$('.jt-address-autocomplete').jt_address();
};
I have already put :address in my user_params in the users controller and has_address :address in the user model
I also have put my Google Api.
My actual error:
undefined local variable or method `street' for #<#Class:0x00007fe91ec7f768:0x00007fe91dfaefc0>
Thanks in advance for your help.
No more error but a bug, maybe because of Google Map, I don't know how
to resolve it, I can't write the address and there is an error message:
problem
Here the code of the inspector:
First, you should add fields like in documentation or scope them using select, now you have code that will create duplicated hidden fields.
You should check from rails console that method address is available on User instance of model.` Maybe you forgot to run migration or didn't restart server after installing this gem as this is js gem and will need restart.
Hope this answers your questions. Add comment to this answer if problem is still there.

Generate several model rows from a date range in rails 5.0.0.1

I want to generate several scheduled events from a date range on selected days input in a form for a specific trainer, The Evento model is:
class Evento < ApplicationRecord
belongs_to :equipo
has_many :asistencias, dependent: :destroy
accepts_nested_attributes_for :asistencias
scope :done, -> { where(registrado: true) }
validates :equipo_id, :fecha, :tipoEvento, presence: true
end
so I have a view at 'app/views/eventos/forma_prog.html.erb' which includes a form to get the parameters to define the date range like this:
<% provide(:title, "Entrenamientos") %>
<div class="col-sm-6 col-sm-offset-3">
<p id="notice"><%= notice %></p>
<h1>Programación de Entrenamientos</h1>
<%= form_tag(programa_path) do %>
<% dias = [] %>
<%= label_tag(:entrenador, "Trainer:") %>
<%= select_tag :entrenador,
options_from_collection_for_select(#entrenadores, "id", "name"),
prompt: "Select the trainer", class: 'form-control' %>
<b>Days:</b>
<div class="well">
<% #dias.each_with_index do |day, index| %>
<%= label_tag day, day, class: "checkbox-inline nopadding"; %>
<%= check_box_tag 'dias[]', index, checked = false, class:
"nopadding" %> |
<% end %>
</div>
<%= label_tag(:inicio, "Starting Date:") %>
<%= date_field_tag :inicio, class: 'form-control' %>
<%= label_tag(:final, "Ending Date:") %>
<%= date_field_tag :final, class: 'form-control' %>
<%= submit_tag "Create events", class: "btn btn-default" %>
<% end %>
</div>
It works fine and returns params:
{"utf8"=>"✓",
"authenticity_token"=>"10BsOFEsCvO...==",
"entrenador"=>"2",
"dias"=>["2", "4"], # Being Tuesday and Thursday
"inicio"=>"2017-06-28",
"final"=>"2017-06-30",
"commit"=>"Create events"}
The controller action that generates the form is: eventos#forma_prog
# GET /eventos/forma_prog
def forma_prog
#entrenadores = User.all
#dias = %w[Dom Lun Mar Mie Jue Vie Sab]
#evento = Evento.new
end
And the controller action that is supposed to create the events is eventos#programa:
# POST /eventos/programa
def programa
entrenador = User.find(params[:entrenador])
inicio = Date.parse(params[:inicio])
final = Date.parse(params[:final])
dias = params[:dias].map! {|ele| ele.to_i }
#tipoEvento = "Entrenamiento"
entrenador.equipos.each do |equipo|
for #fecha in (inicio..final) do
if dias.include?(#fecha.wday)
#equipo_id = equipo.id
#evento = Evento.new(evento_params)
if !#evento.save
flash[:error] = "No ha sido posible crear los eventos."
redirect_to root_path
end
end
end
end
end
When I click the submit button I get an error saying param is missing or the value is empty: evento and the app console points to the eventos#evento_params:
# Never trust big bad internet, always use strong params
def evento_params
params.require(:evento).permit(:fecha, :tipoEvento, :equipo_id, :comment, :registrado, :asistencias_attributes => [:evento_id, :player_id, :tipo, :comment])
end
I can see that the params.require(:evento) part is the problem and I guess it has something to do with the 'form_tag' I chose instead a 'form_for #evento' But I did this way because I think the form is not fully related with the #evento object for the model, please help me here...
You are correct, the first problem is that form_tag doesn't group all evento values in one key, as you are expecting.
This can be solved using form_for but, if you would like to keep form_tag, just rename the inputs with evento[attribute].
Where attribute is the name of the parameter/field (e.g. evento[fecha] for your input fecha).
After fixing the above, the error you mention will go away, but will another one will be raised because none of the attributes needed to create an Evento is sent in your form.
So, the second problem is that the form is not sending all the parameters; you can see those needed parameters in evento_params method:
def evento_params
params.require(:evento).permit(:fecha, :tipoEvento, :equipo_id, :comment, :registrado, :asistencias_attributes => [:evento_id, :player_id, :tipo, :comment])
end
This means that evento_params is expecting a Parameters hash with this structure:
{
"utf8"=>"✓",
"authenticity_token"=>"...",
"evento"=>{
"fecha"=>"...",
"tipoEvento"=>"...",
"equipo_id"=>"...",
"comment"=>"...",
"registrado"=>"...",
"asistencias_attributes"=>[
{
evento_id=>"...",
player_id=>"...",
tipo=>"...",
comment=> "..."
}
],
"commit"=>"Create events"
}
To fix this, you either need to send all those missing parameters, or adjust evento_params method to use only the ones sent by the form.

Creating better error messages in the View for Rails

I am experimenting with a simple people/new page to practice showing error messages. Right now, with the below set-up, the error messages display okay but look a little awkward because the text says "Firstname can't be blank", "Lastname can't be blank" and so forth. Basically I want the text to say "First Name can't be blank" and "Last Name can't be blank" (with spaces), but I'm assuming the error messages are just following the attribute names I defined in the Model, and there's really no way to change those explicitly.
So how can I change that? Can I not achieve that change without making changes to this particular partial? (shared_error_messages)
Thanks,
Screenshot of the new view below.
people_controller.rb
class PeopleController < ApplicationController
def new
#person = Person.new
#people = Person.all
end
def create
#person = Person.new(person_params)
if #person.save
redirect_to new_person_path
else
render 'new'
end
end
private
def person_params
params.require(:person).permit(:firstname, :lastname, :age)
end
end
person.rb
class Person < ActiveRecord::Base
validates :firstname, presence: true, length: {maximum: 15}, format: { with: /\A[a-zA-Z]+\z/,
message: "only allows letters" }
validates :lastname, presence: true, length: {maximum: 15}, format: { with: /\A[a-zA-Z]+\z/,
message: "only allows letters" }
validates :age, presence: true, length: {maximum: 3}
end
new.html.erb
Enter your information
<hr>
<%= form_for #person do |f| %>
<%= render 'shared/error_messages', object: f.object %>
First Name: <%= f.text_field :firstname %><br>
Last Name: <%= f.text_field :lastname %><br>
Age: <%= f.number_field :age %><br>
<%= f.submit "See Your Life Clock" %>
<% end %>
shared/_error_messages
<% if object.errors.any? %>
<div id="error_explain">
<h2><%= pluralize(object.errors.count, "error") %> prohibited this from being saved to DB</h2>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Assuming you're using Rails 3.2 or later, the "best" way to do this is with localization. Edit your en.yml file and add this:
en:
activerecord:
attributes:
person:
firstname: "First Name"
lastname: "Last Name"
The error reporting system will look up human-friendly names for your attributes from the localization file. Thie may seem heavy-handed, but in my opinion it is the best way because these names will be accessible anywhere, centralized, and easy to maintain.
If you need the friendly name in your own code, (for example, in a label on an edit form or a column header in a list table) you can access it like this:
Person.human_attribute_name(:firstname)
Now if you change the en.yml, the change will reflect throughout your site. And should you ever need to localize to another language, you're on the right track already.
If this is too much for you, you can change your validations to have complete sentences for their messages, and then change your _error_messages partial to look like this:
<% if object.errors.any? %>
<div id="error_explain">
<h2><%= pluralize(object.errors.count, "error") %> prohibited this from being saved to DB</h2>
<ul>
<% object.errors.each do |attr, msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
An old question, but may be someone facing the same problem benefit from it, so I'm answering it.
In the view you used something like First Name: <%= f.text_field :firstname %> better to use something as follows which will give you good field names by default in your situation.
<%= f.label :firstname, 'First Name' %><br>
<%= f.text_field :firstname %>

Resources