Background: I would like to make a team and have the user verify the address of that team before the team is saved.
In my application I have a form that creates a team when the form is submitted. Within this form I have a partial that is suppose to render with a field location. When the user clicks submit within the partial form the location field (within the partial form and not the create team form) should go to the verify_Address action within the teams_controller. Instead of this happening I get an error when I load the page.
The error on pageload:
undefined local variable or method `verify_address' for #<#<Class:0x000001063ec8d8>:0x00000104555af0>
with this line highlighted: <%= form_for :team, url: verify_address, method: :post, remote:true do |f|%>
below are my files within the app.
route.rb file:
resources :users, :path => :captains, :as => :captains, :controller => "captains" do
resources :teams, :only => [:new, :create, :edit, :update, :destroy], controller: 'captains/teams'
end
resources :teams, :only => [:index, :show] do
resources :users, :path => :teammates, :as => :teammates, :controller => "teammates"
end
put 'captains/:id/teams/verify_address' => "teams#verify_address",as: 'verify_address'
get 'captains/:id/teams/verify_address' => "teams#verify_address"
controller/captains/teams_controller.rb:
class Captains::TeamsController < ApplicationController
respond_to :html, :js
def new
#team = Team.new
#captain = current_user
end
def verify_address
#address = params[:team][:location]
#validate_address = Team.validate_address(#address)
end
def create
#captain = current_user.id
#team = Team.create(
:name => params[:team][:name],
:location => params[:team][:location],
:sport => params[:team][:sport],
:captain_id => #captain,
:avatar => params[:team][:avatar]
)
if #team.present?
redirect_to #team # show view for team
end
binding.pry
end
end
the partial views/captains/teams/_verify_address.html.erb:
<%= form_for :team, url: verify_address, method: :post, remote:true do |f|%>
<div class = "form-group">
<%= f.label :location %>
<%= f.text_field :location, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.submit "Verify address" ,class: 'btn btn-success' ,id: 'verify_address' %>
</div>
<% end %>
the main form views/captains/teams/new.html.erb:
<%= form_for :team, url: captain_teams_path(#captain, #team), method: :post do |f|
%>
<div class="form-group">
<%= f.label :avatar %>
<%= f.file_field :avatar %>
<%= f.hidden_field :avatar_cache %>
</div>
<div class = "form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.label :sport %>
<%= f.text_field :sport, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.label :location %>
<%= f.text_field :location, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.submit class: 'btn btn-success' ,id: 'team_role_submit' %>
</div>
<% end %>
</div>
<%= render partial: "/captains/teams/verify_address", locals: { address: #address, validate_address: #validate_address}%>
</div>
Creating a custom route verify_address generates verify_address_path url helper, which you should use in your form.
Related
been banging my head on the table over this one. I have another nested form but this one is driving me crazy.
The error is:
TypeError in CompaniesController#create
no implicit conversion of Symbol into Integer
#company = Company.new(company_params)
The controller:
class CompaniesController < ApplicationController
layout 'welcome'
def new
#company = Company.new
end
def create
#company = Company.new(company_params)
if #company.save
flash[:notice] = "New company created successful."
redirect_to admin_accounts_path
else
flash.now[:alert] = "Creation failed, please try again"
render :new
end
end
private
def company_params
params.require(:company).permit(:name, :location, :users_attributes => [:email, :password])
end
end
On the new.html.erb:
<%= render partial: 'form', locals: { company: #company, users_attributes: :users_attributes } %>
This code looks exactly like another nested setup I have, but it works :p
I had read that sometimes changing the params from => to just having a semicolon works, but replacing the user_attributes => with a user_attributes: didn't change anything.
EDIT: form.html.erb
<%= form_for company, url: companies_path do |f| %>
<div class="col-12">
<div class="row">
<div class="col p-0 mr-3">
<div class="form-group">
Company <%= f.label :name %>
<%= f.text_field :name, :placeholder => 'Company name', class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :location %>
<%= f.text_field :location, :placeholder => 'Location', class: 'form-control' %>
</div>
</div>
<div class="col p-0">
<%= f.fields_for users_attributes do |user_f| %>
<div class="form-group">
<%= user_f.label :email %>
<%= user_f.text_field :email, :placeholder => 'Your Email Address', class: 'form-control' %>
</div>
<div class="form-group">
<%= user_f.label :password %>
<%= user_f.password_field :password, :placeholder => 'Password', class: 'form-control' %>
</div>
<% end %>
</div>
<div class="col-12 p-0">
<%= f.submit "Sign-Up", :class => 'btn btn-primary btn-block btn-lg' %>
</div>
</div>
</div>
<% end %>
Use users instead of users_attributes on the form.
Don't forget to define accepts_nested_attributes_for :users in model Company
## new.html.erb
<%= render partial: 'form', locals: { company: #company, users_attributes: :users } %>
I wonder why you didn't put :users into the form directly
I've currently got an upload form that takes an epub file, parses it using epubinfo and then displays in another form to validate the information we're extracting is correct. We then submit this new upload from this form.
This first form (upload.html.erb) seems to work exactly as I'd expect, extracting all the information I required, including the cover image and displays it appropriately in the edit/verification form (new.html.erb). However, the issue lies in this second form (new). Upon posting it doesn't retain the cover image that was displayed, instead not saving anything, even though any other changed (or unchanged) data in this form posts just as it should.
To note, when the upload form posts, public/tmp gets two files, the epub and the png, and upon posting the new form, the epub tmp goes away and gets put into public, however the png stays in public/tmp.
Ruby 2.1.1p76
Rails 4.1
CarrierWave 0.10.0
/views/pages/upload.html.erb
<%= form_for Book.new, :url => new_book_path do |f| %>
<div class="form-group">
<%= f.label :book, "Upload an epub" %>
<%= f.file_field :book, class: "form-control" %>
<%= f.hidden_field :book_cache %>
</div>
<br />
<div class="form-group">
<%= f.button "Upload", class: "btn btn-primary", data: {disable_with: "Uploading..."} %>
</div>
<% end %>
/app/controllers/books_controller.rb (somewhat sanitized):
class BooksController < ApplicationController
before_action :redirect_to_book, only: [:index]
before_action :set_book, only: [:show, :edit, :update, :destroy, :download]
before_action :is_mod, only: [:edit, :update, :destroy]
def create
#books = Book.new(book_params)
if #books.author.nil? && book_params[:author_name].present?
#books.author = Author.where(name: book_params[:author_name]).first_or_create
end
if #books.series.nil? && book_params[:series_name].present?
#books.series = Series.where(name: book_params[:series_name]).first_or_create
end
if book_params[:is_public].present?
else
#books.is_public == true
end
if #books.save
redirect_to book_path(#books), success: "The book was successfully saved!"
else
redirect_to new_book_path
end
end
def new
#authors = Author.all
#series = Series.all
#books = Book.new(book_params)
#books.extract_epub_info
end
private
def set_book
#books = Book.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to "/404"
end
def redirect_to_book
redirect_to book_path(params[:book_id]) if params[:book_id]
end
def book_params
params.require(:book).permit(:book, :cover, :title, :isbn, :summary, :is_public, :series_nbr, :author_id, :author_name, :series_id, :series_name, :tag_list, :book_cache)
end
def is_mod
unless current_mod
redirect_to book_path, error: "You're not allowed to edit books."
end
end
end
/app/uploaders/book_uploader.rb
class BookUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{mounted_as}"
end
def filename
"#{model.id}.epub"
end
end
/app/uploaders/cover_uploader.rb
class CoverUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{mounted_as}"
end
def filename
"#{model.id}.png"
end
end
/app/models/book.rb
class Book < ActiveRecord::Base
belongs_to :author
belongs_to :series
has_many :downloads
has_many :flags
has_many :ratings
has_many :raters, :through => :ratings, :source => :users
validates :title, :author_id, presence: true
attr_accessor :author_name, :series_name
acts_as_taggable
scope :public_books, ->{ where(is_public: true) }
mount_uploader :book, BookUploader
# validates_presence_of :book
mount_uploader :cover, CoverUploader
# validates_presence_of :cover
searchable do
text :title
text :summary
text :isbn
integer :is_public
end
def epub
#epub ||= EPUBInfo.get(book.file.path)
end
def extract_epub_info
if epub.creators.first.name.include? ","
self.author = Author.where(name: epub.creators.first.name).first_or_create
else
parts = epub.creators.first.name.split
first = parts.first
last = parts[1..-1].join(" ")
self.author = Author.where(name: "#{last}, #{first}").first_or_create
end
self.title = epub.titles.first
self.summary = epub.description
self.is_public = true
self.isbn = epub.identifiers.find{ |i| i.scheme == "ISBN" }.try(:identifier)
if epub.cover.present?
self.cover = epub.cover.tempfile
else
end
end
end
And the edit form that displays the cover appropriately but doesn't post when you submit the form.
/views/books/new.html.erb:
<%= form_for #books, url: books_path, html: {multipart: true} do |f| %>
<% if #books.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#books.errors.count, "error") %> prohibited this author from being saved:</h2>
<ul>
<% #books.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<br/><br/>
<div class="alert alert-warning">
<strong>Please validate that all of this information is correct before saving the book!</strong>
</div>
<br/>
<% if #books.cover.present? %>
<div align="center">
<%= image_tag #books.cover %>
</div>
<% end %>
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :author %>
<%= f.collection_select(:author_id, #authors.order("name ASC"), :id, :name, {:include_blank => true} ) %>
OR New Author Name:
<%= f.text_field :author_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :isbn %>
<%= f.text_field :isbn, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :summary %>
<%= f.text_area :summary, class: "form-control", size: "20x10" %>
</div>
<div class="form-group">
<%= f.label :series_nbr %>
<%= f.text_field :series_nbr, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :series %>
<%= f.collection_select(:series_id, #series.order("name ASC"), :id, :name, {:include_blank => true} ) %>
OR New Series Name:
<%= f.text_field :series_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :tag_list %>
<%= f.text_field :tag_list, class: "form-control" %>
</div>
<%= f.hidden_field :book_cache %>
<div class="form-group">
<%= f.label :cover, "Upload a cover" %>
<%= f.file_field :cover, class: "form-control" %>
</div>
<br/>
<div class="form-group">
<%= f.hidden_field :is_public, :value => "1" %>
<%= f.submit class: "btn btn-primary", data: { confirm: "Are you sure all of this information is correct?\rYou will not be able to edit it later." } %>
</div>
<% end %>
EDIT for routes 11/2 # 9:20ET
routes.rb
App::Application.routes.draw do
root "pages#home"
# All pages
get "browse" => "books"
get "browse/new" => "books#newest"
get "browse/popular" => "books#popular"
get "browse/random" => "books#random"
get "faq" => "pages#faq"
get "empowerment" => "pages#empowerment"
get "legal" => "pages#legal"
get "pages/catalog"
get "search" => "search#search"
get "search/search"
get "upload" => "pages#upload"
get "download" => "pages#download"
get "dmca" => "pages#dmca"
# Catalog Routes
get "catalog" => "catalog#index", defaults: {format: :atom}
get "catalog/search" => "catalog#search", defaults: {format: :atom}
%w{ newest popular random author title serie tag }.each do |section|
get "catalog/#{section}", controller: :catalog, action: section, defaults: {format: :atom}
end
get "catalog/author/:id" => "catalog#authored_by", defaults: {format: :atom}
get "catalog/tag/:tag" => "catalog#tagged", defaults: {format: :atom}
# Normal Model stuff
resources :authors
resources :series
resources :books do
collection do
get "tagged/:tag", action: :tagged, as: :tagged
post "new", action: :new, as: :new_book
end
member do
get :download
end
end
resources :tags
# Stuff for Mods and management
devise_for :mods
namespace "admin" do
resources :mods
end
devise_scope :mod do
get "mods" => "devise/sessions#new"
end
# Lists for dropdowns
get "authorlist" => "books#authorlist"
get "serieslist" => "books#serieslist"
get "taglist" => "books#taglist"
# Let's be nice and support old urls.
get "book/show/id/:id" => "books#show"
get "serie/:id" => "series#show"
get "author/:id" => "authors#show"
get "browse/author/:id" => "authors#show"
end
I'm not sure what else would be needed. I feel that what I'm missing is simple, but after staring at it for so long I've just stopped making any progress. I know that the cover image shows up in new.html.erb because it displays in the image_tag in the form, however upon post it doesn't save the param.
Halp! :(
/views/pages/upload.html.erb -- missing multipart: true
<%= form_for Book.new, html: { multipart: true } do |f| %>
I don't think you need to supply the url argument unless you've set up your routes/models incorrectly. You should change this in your other forms as well.
/app/controllers/books_controller.rb
You're completely missing edit/update methods. Is this by design? Why? Is this why you're supplying the url option in the forms? This promotes confusion.
Have you viewed the contents of book_params to see if the image data is being received correctly? What about checking to see if the actual image files have been created? If they are created, perhaps they're just not accessible (check file permissions).
FURTHER EDIT: I can't determine how things are routed based on what you've shown. Please show your routes as well, and clearly describe which actions result in the unwanted consequences.
i.e., "I want to create a new book, but the uploaded image isn't saved" (meaning it doesn't exist in the database and there's no actual image anywhere on the disk).
or
"I can create a book and save the image, but upon viewing the EDIT page, the image doesn't show up"
I have two controllers categories and products. Products belongs to categories but I'm having trouble setting up the relationship.
In category.rb
has_many :products
In product.rb
belongs_to :category
validates :category_id, :presence => true
validates :name, :presence => true, :uniqueness => true
When I try to create a new product, the record does not save because category_id is blank. My product form looks like so:
<%= form_for #product, :html => { :class => 'form-horizontal' } do |f| %>
<%= f.hidden_field('category_id', :value => params[:category_id]) %>
<div class="control-group">
<%= f.label :name, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :name, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :category, :class => 'control-label' %>
<div class="controls">
<% #cat = Category.all %>
<%= select_tag 'category', options_from_collection_for_select(#cat, 'id', 'name') %>
</div>
</div>
<div class="control-group">
<%= f.label :price, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :price, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :description, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :description, :class => "tinymce", :rows => 10, :cols => 120 %>
<%= tinymce %>
</div>
</div>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
products_path, :class => 'btn' %>
</div>
<% end %>
and in the products controller I have:
def new
#product = Product.new
#category = #product.category
end
I've tried looking through other questions on SO but haven't had any luck finding the correct way to pass the category id to products.
I hope I've been clear enough and I'm happy to provide any extra information that may be needed.
EDIT
I've made the following changes to the products controller as suggested by I not get the error: Cannot find Category without an ID
before_filter :set_category, only: [:create]
def set_category
#category = Category.find(params[:category_id])
end
def create
#product = #category.products.new(product_params)
#....
end
I'm using nested routes like so:
resources :categories do
resources :products
end
You should set product's category in create action:
def create
#product = #category.products.new(product_params)
# ...
end
in new action, you should just have
def create
#product = Product.new
end
Of course, you need to set #category (#category = Category.find(params[:category_id])) instance variable before (in before_filter, for example).
You should also remove this hidden field from view and remove category_id from allowed params if you don't want users to set category_id manually and set form_for arguments properly, since you're using nested resources:
form_for [#category, #product]
I'm trying to add a custom create action for my Book model, but i keep ending up with a "Couldn't find Book without an ID".
routes.rb:
Books::Application.routes.draw do
resources :books
resources :books do
collection do
post 'create_new_record', :action => :create_new_record
end
end
match 'create_new_record' => 'books#create_new_record', via: [:post]
The relevant controller action:
def create_new_record
#book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'New book record created.' }
end
end
end
And my form (in new.html.erb). I'm looping through results that i get from goodreads.com.
<% #book_search.results.work.each do |stuff| %>
<%= form_for(#book, :url => create_new_record_books_path) do |f| %>
<div class="field">
<%= f.label :author %><br>
<%= f.text_field :author, :value => stuff.best_book.author.name %>
</div>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title, :value => stuff.best_book.title %>
</div>
<div class="field">
<%= f.label :isbn %><br>
<%= f.text_field :isbn, :value => stuff.best_book.isbn %>
</div>
<div class="field">
<%= f.label :image %><br>
<%= f.text_field :image, :value => stuff.best_book.image_url %>
</div>
<div class="field">
<%= f.label :bookid %><br>
<%= f.text_field :bookid, :value => stuff.best_book.id %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<hr>
<% end %>
The error i get when submitting the form is:
ActiveRecord::RecordNotFound in BooksController#create_new_record
on the callback
def set_book
#book = Book.find(params[:id])
end
I'm pretty much stumped now, my understanding is that it doesn't even reach the action, but instead looks for a book id that doesn't exist?
Thank you!
If you use before_filter so you don't pass an id to create action. Call your before filter the following way:
before_filter :set_book, except: [:index, :new, :create]
If you use model callback, params is unavailable in the model so pass the id some other way, for example via attr_accessor.
use #book = Book.where(id: params[:id]).first
So I have a self-joining model defined. Basically a post on a forum, and a parent_post that it belongs to.
class Post < ActiveRecord::Base
has_many :replies, :class_name => "Post"
belongs_to :thread, :class_name => "Post", :foreign_key => "parent_post_id"
end
Which seems fundamentally sound. I created a new RESTful route for the reply action, and an action and view.
Routes:
resources :forums do
resources :posts do
member do
get 'reply'
end
end
end
The view layer and the control action seems to be where I'm getting hosed up.
def reply
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build
#post.thread = #forum.posts.find(params[:id])
#post.title = "RE: #{#post.thread.title}"
end
def create
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build(params[:post])
#post.user = current_user
if #post.save
redirect_to forum_post_path(#forum, #post), notice: 'Post was successfully created.'
else
render action: "new"
end
end
And in the view layer I was just trying to use the same scaffold generated form partial I'm using for the standard new and edit actions.
#reply.html.erb
<%= render :partial => 'form' %>
#_form.html.erb
<%= form_for [#forum,#post], :html => { :class => 'form-horizontal' } do |f| %>
<fieldset>
<legend><h1>New Thread</h1></legend>
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'text_field span9' %>
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :body, :class => 'text_area span9' %>
</div>
</div>
<div class="form-actions">
<%= f.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_posts_path(#forum), :class => 'btn' %>
</div>
</fieldset>
<% end %>
However, the parent_post_id is getting lost when I'm creating the post and it's getting set to nil. Do I need to create another action? Is there some other way to set the thread? Some third thing?
This will work:
Reply action:
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build
#post.thread = #forum.posts.find(params[:id])
#post.title = "RE: #{#post.thread.title}"
Then add this to your view
<%= f.hidden_field :parent_post_id, #post.thread.id %>
BTW I question whether you need a custom reply method as opposed to using built-in RESTful methods but this should fix your problem and that wasn't really your question.
add
<%= hidden_field_tag :forum_id , #forum.id %>
to your form
So basically, when you're submitting to the Posts#create action you're submitting a url that looks something like this /forum/1/posts which removes the parent_post_id from the url. Since you're using that parent_post_id to build that url, you need a way to POST with it.
My suggestion is allowing a POST to a reply resource that is nested in the posts resource.
(ie POST /forums/1/posts/1/reply)
So maybe something like this
resources :forums do
resources :posts do
# :show is actually just pointing to a form
resource :reply, :only => [:show, :create],
:controller => 'reply' #otherwise gets routed to 'replies'
end
end
So you would also need a ReplyController but that would basically match your reply method on your post controller with a few changes.
def show
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.find(params[:post_id])
#reply = #forum.posts.build
#reply.thread = #post
#reply.title = "RE: #{#post.thread.title}"
end
def create
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.find(params[:post_id])
#reply = #forum.posts.build(params[:reply])
#reply.thread = #post
#reply.user = current_user
if #reply.save
redirect_to forum_post_path(#forum, #post), notice: 'Reply was successfully created.'
else
render action: "show"
end
end
The biggest problem would be that you would have to abstract your Post fields from your form for block. That's because the url you're trying to POST to is going to be different. But it shouldn't be too bad just doing something like this:
reply/show.html.erb
<%=
form_for #reply, :url => forum_post_reply_path(#forum, #post),
:html => { :class => 'form-horizontal' } do |builder|
%>
<fieldset>
<legend><h1>New Reply</h1></legend>
<%= render "posts/post_fields", :f => builder %>
<div class="form-actions">
<%= builder.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_post_path([#forum, #post]), :class => 'btn' %>
</div>
</fieldset>
<% end %>
posts/_form.html.erb
<%= form_for [#forum,#post], :html => { :class => 'form-horizontal' } do |builder| %>
<fieldset>
<legend><h1>New Thread</h1></legend>
<%= render "post_fields", :f => builder %>
<div class="form-actions">
<%= builder.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_posts_path(#forum), :class => 'btn' %>
</div>
</fieldset>
<% end %>
posts/_post_fields.html.erb
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'text_field span9' %>
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :body, :class => 'text_area span9' %>
</div>
</div>
Note: There's probably a better way to declare the routes than I have, but I don't really know.