I am using a form for "GET" method. I created a text field (in testing.html.erb) for user to enter the name. I want the name to be passed into the controller and based on the name, I want to retrieve his data from the database through a query.
The problem here is that I am not getting anything into the instance variable in the controller action (Nothing is printed on screen when I display #testing in "testing.html.erb"). Below is my routes.rb file.
Rails.application.routes.draw do
resources :users
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
# root 'welcome#index'
# Example of regular route:
get 'index' => 'users#index'
get 'testing' => 'users#testing'
# Example of named route that can be invoked with purchase_url(id: product.id)
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
.....
This is my testing.html.erb file
<h1> Testing page </h1>
<%= form_for users_path, :url => {:action => "testing", :name => :name}, :html => {:method => :get} do |f| %>
<%= f.label :first_name %><br />
<%= f.text_field :name %><br />
<%= f.submit "View Schedule" %>
<% end %>
<%= #testing %>
<%#= #testing.email %>
<%#= #testing.content %>
Please note that I commented #testing.email/content in above file to supress the error (undefined method `email' for nil:NilClass).
Below is my users_controller.rb file.
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
#before_action :user_set, only: [:testing]
# GET /users
# GET /users.json
def index
#users = User.all
#test = params[:name]
#test_index = params[:age]
end
# GET /users/1
# GET /users/1.json
def show
end
def testing
#testing = User.find_by(name: params[:name])
#if #testing.name == "Siri"
# #render text: "Hello #{#testing.name}"
#redirect_to action: :index
#end
end
.........
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :email, :content)
end
end
The log-file shows the following.
Processing by UsersController#testing as HTML
Parameters: {"utf8"=>"✓", "/users"=>{"name"=>"hello rails"}, "commit"=>"View Schedule"}
I also tried to use strong params as User.find_by(name: params[:users][:name]) which throws error "undefined method `[]' for nil:NilClass".
I think I am going wrong somewhere. Please correct me. Thank you for your time.
Issue lies here:
Parameters: {"utf8"=>"✓", "/users"=>{"name"=>"hello rails"}, "commit"=>"View Schedule"}
Do you see /users key in your params? It shoudn't be there. This indicates problem with your form:
<%= form_for users_path, :url => {:action => "testing", :name => :name}, :html => {:method => :get} do |f| %>
First argument is expected to be a string (which is then used as a name of form params) or ActiveModel object. In your case, it is string returned by users_path, which is just '/users'. It should be #testing
<%= form_for #testing, :url => {:action => "testing", :name => :name}, :html => {:method => :get} do |f| %>
That will fix your current issue, you will get another shortly after that, which should go into a separate question.
You'll be best using the following:
#app/views/users/testing.html.erb
<%= #user.try(:name) %>
<%= form_tag users_testing_path, method: :get do |f| %>
<%= f.text_field :name %>
<%= f.submit "View Schedule" %>
<% end %>
This will allow:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def testing
#user = User.find_by name: params[:name]
end
end
You could improve your routes file too:
#config/routes.rb
resources :users, path: "", only: :index, path_names: { index: "index" } do #-> url.com/index
get :testing, on: :collection #-> url.com/testing
end
Related
I'm building my second-ever basic Ruby on Rails application and having fun doing it, but have gotten stuck at precisely the same place that gave me trouble (and was never solved) on my last effort: the PUT or PATCH request.
My application has two models: entries and users. A logged-in user should be able to edit only those entries that were originally created by that user.
CONTROLLER
class EntriesController < ApplicationController
# authenticate user (Devise)
before_action :authenticate_user!, :except => [:index, :show]
# set entry upon page load
before_action :set_entry, :only => [:show, :edit, :update, :destroy]
# GET request - display all entries
def index
#all_entries = Entry.all
end
# GET request - display an individual entry
def show
# nothing required here because entry identified with before_action :set_entry on line 2 above
end
# GET request - access form to create a new entry
def new
#entry = Entry.new
#user = User.find(current_user[:id])
end
# GET request - access form to update an existing entry
def edit
if #entry[:user_id] != current_user[:id]
redirect_to root_path
else
redirect_to edit_entry_path
end
end
# POST request - make a new entry/save new data into db
def create
user = current_user[:id]
Entry.create({
entry_title: params[:entry][:entry_title],
book_title: params[:entry][:book_title],
text: params[:entry][:text],
img_url: params[:entry][:img_url],
tag: params[:entry][:tag],
created_at: params[:entry][:created_at],
user_id: user
})
redirect_to entries_path
end
# PUT request - save changes to an existing entry
def update
if #entry.update(entry_params)
redirect_to entry_path
else
render :new
end
end
# DELETE request - delete an existing entry from db
def destroy
#entry.destroy
redirect_to entries_path
end
private
def set_entry
#entry = Entry.find(params[:id])
end
def entry_params
params.require(:entry).permit(:email, :text, :tag)
end
end
VIEW (show.html.erb - shows a single entry and includes links allowing the logged-in user who originally authored the entry to edit or delete it)
<h3>Selected Entry</h3>
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6">
<div>Entry title: <%= #entry.entry_title %></div>
<div>Book title: <%= #entry.book_title %></div>
<div>Text: <%= #entry.text %></div>
</div>
<div class="col-md-4">
<div><%= #entry.created_at.strftime("%b %d, %Y") %></div>
<div>Submitted by: <i><%= #entry.user.email %></i></div>
<div>File under: <i><%= #entry.tag %></i></div>
<% if current_user %>
<%= link_to 'Edit', #entry, :method => 'update' %>
<%= link_to 'Delete', #entry, :method => 'delete' %>
<% end %>
</div>
</div>
ROUTES.RB - At first my routes were the commented-out lines, but then I had a thought that was either madness or sudden realization - should only the GET routes lead with "get"? So that's the non-commented-out attempt you see. Somehow the app works (except for the issue at hand) both ways.
In researching I've come across routes defined using a much more elaborate syntax than that I'm using here. I've been unable to figure out whether a given way of doing things is different convention, outdated, or just inadequate to the task.
Rails.application.routes.draw do
devise_for :users
resources :entries
# root 'entries#index'
# get '/entries' => 'entries#index'
# get '/users' => 'users#index'
# get '/entries/:id' => 'entries#show'
# get '/entries/:id' => 'entries#update'
# get '/entries/new' => 'entries#new'
# get '/entries/:id/edit' => 'entries#edit'
# get '/users/:id' => 'users#show'
# get '/about' => 'pages#index'
root 'entries#index'
get '/entries' => 'entries#index'
get '/entries/new' => 'entries#new'
post '/entries' => 'entries#create'
get '/entries/:id' => 'entries#show'
get '/entries/:id/edit' => 'entries#edit'
put '/entries/:id' => 'entries#update'
delete '/entries/:id' => 'entries#destroy'
get '/users' => 'users#index'
get '/users/:id' => 'users#show'
get '/about' => 'pages#index'
end
Thanks in advance for any insight. If additional context is needed I'm happy to provide.
Edited to add:
PARTIAL (_form.html.erb)
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6" id="form-container">
<%= form_for #entry do |form| %>
<br>
<%= form.text_field :entry_title, :size => 59, :placeholder => "Entry Title"%>
<br><br>
<%= form.text_field :book_title, :size => 59, :placeholder => "Book Title"%>
<br><br>
<%= form.text_field :img_url, :size => 59, :placeholder => "Image URL"%>
<br><br>
<%= form.text_area :text, :placeholder => "Text" %>
<br><br>
<%= form.text_field :tag, :placeholder => "Tag" %>
<br><br>
<%= form.submit %>
<% end %>
</div>
<div class="col-md-4"></div>
</div>
To edit a record you
first, should use a GET request to get the edit form
second, should submit that form using a PUT/PATCH request
To get to the edit form you should link to the edit path for your entry
<%= link_to 'Edit', edit_entry_path(#entry) %>
The Rails form helpers will automatically set the form to submit with the proper method, PUT OR PATCH.
:method in link_to helpers refers to HTML verb (get, post, etc), while controllers methods naming convention is action.
link_to
You need something as
<%= link_to 'Edit', #entry, :method => 'put' %>
or
<%= link_to 'Edit', #entry, :action => 'update' %>
At a glance you are trying to post with the edit link. Remember new/edit are get methods to render form, so just just delete method part in your links. Like from
<%= link_to 'Edit', #entry, :method => 'update' %>
to
<%= link_to 'Edit', edit_entry_path(#entry) %>
I'm building my second-ever basic Ruby on Rails application
Congrats! You need at least 3 more before it all starts to make sense
To add to the existing answers, you'll be best looking at the resources directive to clean the routes up:
#config/routes.rb
root 'entries#index'
devise_for :users
resources :entries
resources :pages, only: [:index], path_names: { index: "about" }
resources :users, only: [:index,:show]
--
A logged-in user should be able to edit only those entries that were originally created by that user.
This is known as authorization.
Authentication = is user logged in?
Authorization = can user do this?
Although people confuse Devise with being able to handle authorization, it only handles authentication. Whilst you have a simple implementation of this in your controller, you should check out either the CanCanCan or Pundit gems:
#Gemfile
gem "cancancan"
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :manage, Post, user_id: user.id
end
end
#app/controllers/entries_controller.rb
class EntriesController < ApplicationController
def edit
authorize! :edit, #entry
end
end
--
Finally, to answer your question directly, you're calling the update method (which doesn't exist) to access the edit view:
<% if current_user %>
<%= link_to 'Edit', #entry, :method => 'update' %>
<%= link_to 'Delete', #entry, :method => 'delete' %>
<% end %>
You should read up about http verbs - this is what the "method" option invokes with the link. As mentioned above, you don't need to set the method for edit as it uses GET. Update uses put/patch, which I can explain later.
A much better way to achieve what you want would be the following:
<%= link_to "Edit", edit_entry_path(#entry) if can? :edit, #entry %>
<%= link_to "Delete", #entry, method: :delete, if can? :destroy, #entry %>
The above uses the CanCanCan authorization method can?
So this is my first attempt kind of just going without tutorial direction on building a site on RoR. Running into some trouble setting up my email signup form. I think there is an issue with how my model is setup or connected but not too sure. Getting "undefined method `model_name' for NilClass:Class" as the error".
signup.rb
class Signup < ActiveRecord::Base
validates_presence_of :email
def change
create_tabe :quotes do |t|
t.string :email
end
end
end
signups_controller.rb
class SignupsController < ApplicationController
def new
#signup = Signup.new
end
def create
#signup = Signup.new(secure_params)
if #signup.valid?
redirect_to root_path
else
render :new
end
private
def secure_params
params.require(:signup).permit(:email)
end
end
simple form section of the view
<%= simple_form_for #signup do |f| %>
<%= f.input :email %>
<%= f.submit 'Signup', :class => 'btn btn-success' %>
<% end %>
routes.rb
Rails.application.routes.draw do
root 'pages#index'
get '/about' => 'pages#about'
get '/tour' => 'pages#tour'
get '/music' => 'pages#music'
resources :signups, only: [:new, :create]
end
Thanks for any and all help!
<%= simple_form_for #signup do |f| %>
to
<%= simple_form_for :signup do |f| %>
For now it will not throw any error because of symbol :signup but for submit the form you need the action attributes for that you can use like url: { action: "create" } click for more detail
Hope this help you.
Hello i'm new one in ruby on rails. I have strange problem. I use some tutorial and get error which i shouldn't get.
I have controller
class DiaryController < ApplicationController
before_action :authenticate_user!
respond_to :html, :xml, :json
respond_to :js, :only => [:create, :update, :destroy]
def create
#record = Record.create(record_params)
#record.userId=current_user.id
if request.xhr? || remotipart_submitted?
sleep 1 if params[:pause]
render :layout => false, :template => (params[:template] == 'escape' ? 'comments/escape_test' : 'diary/create'), :status => (#record.errors.any? ? :unprocessable_entity : :ok)
else
redirect_to diary_path
end
end
def add
#record = Record.new
#respond_with(#record, :layout => false)
respond_with do |format|
format.html { render :layout => ! request.xhr? }
end
end
# PUT /comments/1
# PUT /comments/1.xml
def update
#record = Record.find(params[:id])
respond_with do |format|
format.html{ redirect_to #record }
end
end
def delete
#comment = Comment.destroy(params[:id])
end
def edit
#record = Record.find(params[:id])
end
def index
#records = Record.where(userId: current_user.id)
end
private
def record_params
params.require(:record).permit(:photo, :comment, :date, :photo_cache)
end
end
Have view
<h1 align="centre">
Добавить запись
</h1>
<%= render 'form' %>
<%= link_to 'Отмена', diary_path, :id => 'cancel-button' %>
and
<%= form_for(#record, :remote => (params[:action] == 'add' ? true : false)) do |f| %>
<fieldset>
<div class="field">
<%= f.label :date, :class => 'required' %><br />
<%= f.date_select :date %>
</div>
<div class="field">
<%= f.label :comment %><br />
<%= f.text_area :comment %>
</div>
<div class="field">
<%= image_tag(#record.photo_url(:thumb)) if #record.photo? %><br />
<%= f.label :photo %><br/>
<%= f.file_field :photo %><br/>
<%= f.hidden_field :photo_cache %>
</div>
</fieldset>
<table>
<tr>
<td>
<div class="actions">
<%= f.submit "Добавить", :data => {:'disable-with' => "Submitting..."} %>
</div>
</td>
<td>
<%= link_to 'Отмена', diary_path, :id => 'cancel-button' %>
</td>
</tr>
</table>
And get ActionView::Template::Error (undefined method `records_path' for #<#:0x000000054461c8>): error on "<%= form_for(#record, :remote => (params[:action] == 'add' ? true : false)) do |f| %>" line. Even records_path i did'n use.
I Have routes
devise_for :users
get 'welcome/index'
root 'welcome#index'
get 'diary' => 'diary#index'
get 'diary/add_record', to: 'diary#add', as: 'add_record'
post 'diary/add_record', to: 'diary#create'
get 'diary/edit_record/:id', to: 'diary#edit'
delete 'diary/edit_record/:id' => 'diary#delete
And and try to use add_record route. Maybe it would be better to use resources :records.But i want to figure out why my routes doesn't work.
view name "diary".
Because you're new to RoR, let me explain why you're receiving the error
form_for
form_for is the likely reason why you're receiving this error (oh, I just saw it actually states this is where the error occurs - sweet)
The problem you have is that form_for is meant as a way to render a form around an ActiveRecord object. It's mean to give some semi-persistence to the data, by using AR in both the new and create actions (allowing you to show the in-putted data on the form after submission)
When you pass an object to form_for, Rails automatically "builds" the form from the ActiveRecord object, one of the options it uses being the url
--
Routes
The problem you have is the object you pass to the form_for takes the model_name attribute to build the route. This means if you want to use the form_for method by just passing an object, it's going to look for routes pertaining directly to that object
If you don't have any [model]_path route set up, you'll likely receive the error you're getting. The fix firstly involves the routes, and secondly involves the controller:
#config/routes.rb
root 'welcome#index'
devise_for :users
resources :diary, path_names: { new: "add_record", create: "add_record", edit: "edit_record", destroy: "edit_record" }
resources :welcome, only: :index
This is down to the idea that Rails' routing structure is built around resources - every route you have should lead to a specific controller action. Whilst including custom actions is completely fine, you have to appreciate that the basis of the routing structure is to construct resourceful routing, which essentially means that Rails perceives every controller / model to have corresponding routes:
--
URL
The second thing to observe is the url of the form
If you have your routes set up as above, and if your routing structure differs from your model structure (different names), you'll want to use the following setup to define the url explicitly:
<%= form_for #record, url: your_custom_path do |f| %>
I'm setting my new Rails website in more than one language, and I'm having problems with the routes. I'm following the instruction of the book 'Agile web development with Rails 4'.
The browser print me this error but I can see that routes are created correctly, so:
What am I doing wrong? (At the end of this message I'll attach all my routes)
No route matches [POST] "/en/home"
When I try putting the routes directly in the browser ("localhost:3000/en" OR "localhost:3000/es") everything works OK. The error prints only when I change my language's switcher. That's why I think the routes are correctly set, and I think is a problem of my switcher or the controller...?
This is the code in the application.html.rb (basically a switcher between languages):
<%= form_tag home_path, class: 'locale' do %>
<%= select_tag 'set_locale',
options_for_select(LANGUAGES, I18n.locale.to_s),
onchange: 'this.form.submit()' %>
<%= submit_tag 'submit' %>
<%= javascript_tag "$('.locale input').hide()" %>
<% end %>
This is the configuration of my routes.rb file:
Group::Application.routes.draw do
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
scope '(:locale)' do
resources :posts
resources :contacts
root "posts#home"
get "/home" => "posts#home"
get "/contact" => "contacts#new"
# static pages
get "/investment" => "contents#investment"
get "/partner-with-us" => "contents#partner", as: "partner"
get "/our-companies" => "contents#companies", as: "companies"
get "/site-map" => "contents#sitemap", as: "sitemap"
get "/terms-and-conditions" => "contents#terms", as: "terms"
get "/privacy" => "contents#privacy"
end
end
This is a file created in /config/initializers/i18n.rb:
#encoding: utf-8
I18n.default_locale = :en
LANGUAGES = [
['English', 'en'],
["Español".html_safe, 'es']
]
And finally, this is the code for my posts_controller.rb, because here is where I create an action "home" in order to put the last post in the home page:
class PostsController < ApplicationController
def index
#posts = Post.all.order("created_at desc")
end
def show
#post = Post.find(params[:id])
end
def home
if params[:set_locale]
redirect_to home_url(locale: params[:set_locale])
else
#posts = Post.all.order("created_at desc")
end
end
end
try to add :method => :get to your form, like this
<%= form_tag home_path, class: 'locale', :method => :get do %>
I'm having difficulties implementing this. Moreover, I come across the "wrong number of arguments (1 for 0)" error on the index page of my controller.
routes.rb
resources :potentialcandidates, :only => [:index, :send, :process]
get 'potentialcandidates/send' => 'potentialcandidates#send', :as => :send_potentialcandidate
post 'potentialcandidates/process' => 'potentialcandidates#process', :as => :process_potentialcandidate
potentialcandidates_controller.rb
class PotentialcandidatesController < ApplicationController
def index
end
def process
#name = params[:name]
#email = params[:email]
# Add user to user model
#user = User.create(email: #email, name: #name, status: "active")
end
end
index.html.erb
Add
send.html.erb
<%= form_tag process_potentialcandidate_path do %>
<%= text_field_tag "name" %>
<%= text_field_tag "email" %>
<%= submit_tag %>
<% end %>
process is a method in ActionController::Base and you do not want to override it in your controller.
Change the name, update your routes and form, and restart the server; it should work just fine.