modifying a user model with a rails form - ruby-on-rails

total rails noob here so bear with me. I've been stuck for a long time trying to figure out how to make a page that allows a logged-in user to change, via text field, certain attributes of the user model. For instance, my user model has a number of "measurement" attributes that i'd like to directly modify via the page. My user model is running on Devise--I'm not sure if i'm doing the right thing by modifying the user model directly.
Here's my controller. I set it to find the second user because I was simply testing it out to see if it worked.
class MeasurementsController < ApplicationController
def index
#person = User.find(2)
end
end
Here's my index.html.erb:
<%= form_for #person do |f| %>
<%= f.label :style %>:
<%= f.text_field :style %><br />
<%= f.submit "Update"%>
<% end %>
However, it spits out this error:
NoMethodError in Measurements#index
undefined method `user_path'
Any help would be REALLY appreciated. Thanks so much in advance. Really lost here.
EDIT: Here's my routes.rb:
Contourfw::Application.routes.draw do
ActiveAdmin.routes(self)
devise_for :admin_users, ActiveAdmin::Devise.config
devise_for :users
root :to => 'contourfw#landingpage'
match "measurements" => "measurements#index"
match "styles" => "styles#index"
end

It looks like you need to add a resourceful route in your config/routes.rb:
resources :users
Update
By default that will assume a controller named users_controller.rb. If you want it to use the controller above, amend that to:
resources :users, :controller => "measurements"
See http://guides.rubyonrails.org/routing.html#customizing-resourceful-routes for more details.

Related

Rails: Error on form_tag custom action

About a year and a half ago I took an online course in Ruby on Rails using version 3.2. I got through the class and made the application and now I'm going back through the class PDFs and doing it in Rails 4.1.0.
I've got to a section where I'm getting an error and I'm not sure if I'm doing something wrong or if something changed from Rails 3.2 to 4.1.
I've got this form_tag tag:
<%= form_tag :action => 'review', :id => #restaurant do %>
<strong>Poster: </strong><%= text_field "review", "poster" %><br /><br />
<strong>Date: </strong><%= datetime_select "review", "date" %><br /><br />
<strong>Review:</strong><br />
<%= text_area "review", "review", :rows => 5 %><br />
<%= submit_tag "Review" %>
<% end %>
The instructions say to get this to work to put the following code in the restaurants_controller.rb file:
def review
Restaurant.find(params[:id]).reviews.create(params[:review])
redirect_to :action=>"show", :id => params[:id]
end
But when I try and view the page that has the for for it I get this error:
No route matches {:action=>"review", "controller=>"restaurants", :id=>#<Restaurant id: 1, name: "Marco & Luca", created_at: "...", updated_at: "..."
My rake routes shows that show is mapped to the restaurants#show is mapped to the restaurant path.
Is the form_tag piece wrong?
In the instructions from the class we didn't set up anything in routes.rb, I'm guessing that's because of the redirect_to line in the controller file. Is that deprecated now? Is that no longer valid?
Routes file as requested: (in the class exercise we didn't have to edit the routes file).
Rails.application.routes.draw do
# You can have the root of your site routed with "root"
root 'restaurants#index'
resources :restaurants do
collection do
get 'login'
get 'register'
post 'newuser'
post 'validate'
post 'search'
end
end
Thanks!
You do need a route for that. The way it works is:
User fills in a form on some page and sends data to review route
Server processes user's data and returns a redirect to show route
A user gets a redirect and requests the show route
A server executes the show action
Apparently the route for review action (or as I called it above: a review route) doesn't exist in routes.rb. Add it.
UPD: Your routes file looks really odd, you seem to not follow a common practice to process one type of resources in one controller. Here, you probably need to add:
...
post 'search'
post 'review'
# ^ THIS ^
end
end
But you already process at least 3 types of resources in one controller: Users, Sessions and Restaurants. This might not be your fault if you were told to do so, but in either case it's not a proper way to structure an application.
Try:
<%= form_tag :action => 'review', :id => #restaurant.id do %>

Ruby on Rails: Using form_for to pass arguments to controllers to create/initilize objects

I'm getting a variety of errors when trying to create a new object, 'Investor' using form_for.
I set it up as simple as possible with only one attribute (:name) but I do not think I'm passing the arguments correctly... or maybe a template needs to be defined in a create.html.rb file? ...or something should be added to config/routes for defining what it means to create, but I'm not really sure what's the solution or what I'm doing incorrectly, although it could be something basic... but I can't seem to solve it or find the proper way to use the form_for to create new objects with multiple params/ args. I was getting the error - Investor/create "template is missing" so I wonder if I need to create a template or not... ?
At the moment I'm having an error in def new #investor = Investor.new, which shouldn't even be used because I'm trying to create an object with arguments rather than one without any, but when I take out the def new from the controller, I get a totally different error saying that there's an argumentError (rb:10)in the form_for line "First argument in form cannot be nil or empty" This is one of the error I had before so I'm starting to feel like I'm spinning my wheels on this, but when I follow the examples I'm getting errors, so maybe someone with more experience could point out my mistakes.
Otherwise, when I send it like Investor.new(:name) or "name" I get a missing stringify_method error, so maybe the problem I'm having is that I don't know the correct way to pass multiple arguments in the controller, but even when I try to just do it with only one there still seems to be a problem creating a new Investor object the way I have it set. Any ideas?
Which is more correct / what's the difference between?:
def create Investor.create(params[]) end
def create Investor.new(params[]) end
I couldn't find any simple examples of how to use the controllers and the model to create the objects using form_for. The tutorial shows simple examples of how to create user objects, but I think the process and the syntax is a bit different when you generate the user model first to create the migrations and then use form_for to create objects of new Users, Investors, or any other entities.
Many details seem to be left out in the online examples and since there are many different ways to do this apparently (tutorial uses (name, options {}) as one example and other examples use args*), well, I couldn't get it to work just right using this scaled back simple example I created so I thought I'd post it up here for some help.
Thanks!
Kameron
Controller:
class InvestorsController < ApplicationController
respond_to :html
def new
#investor = Investor.new
end
def create
#investor = Investor.create(investor_params)
end
def investor_params
allow = [:name]
params.require(investor).permit(allow)
end
end
Model:
class Investor < ActiveRecord::Base
include ActiveModel::Validations
attr_accessor :name
validates :name, presence: true
end
end
<%= form_for #investor do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit "Create my account" %>
<% end %>
Routes:
Tycoon::Application.routes.draw do
resources :investors, only: [:new,:create]
root 'static_pages#home'
match '/help', to: 'static_pages#help', via: 'get'
match '/investors/new', to: 'investors#new', via: 'get'
match '/about', to: 'static_pages#about', via: 'get'
match '/contact', to: 'static_pages#contact', via: 'get'
get "static_pages/home"
Try this.
Controller:
class InvestorsController < ApplicationController
respond_to :html
def new
#investor = Investor.new
end
def create
#investor = Investor.create(investor_params)
end
private
def investor_params
params.require(:investor).permit(:name)
end
end
Model:
class Investor < ActiveRecord::Base
validates :name, presence: true
end
View:
<%= form_for #investor do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit "Create my account" %>
<% end %>
Routes:
Tycoon::Application.routes.draw do
resources :investors, only: [:new,:create]
root 'static_pages#home'
match '/help', to: 'static_pages#help', via: 'get'
match '/about', to: 'static_pages#about', via: 'get'
match '/contact', to: 'static_pages#contact', via: 'get'
get "static_pages/home"
end

Rails 4 Create form to update only one attribute

I have a user model, but I need to create a form to update only one attribute.
EDIT: I added the create_mailbox patch in :users resource but the form throws a undefined methodsettings_create_mailbox_path'` error. Can anyone give me some insight to how this member patch/resource routes work?
Here's the form:
<%= form_for #user, url: settings_create_mailbox_path(#user) do |f| %>
<%= f.text_field :rss_mailbox, autocomplete: 'off'%>
<button type="submit" class="button">Create Mailbox</button>
<% end %>
Here's the Users route:
resources :users, id: /.*/ do
member do
patch :settings_update, controller: :settings
patch :create_mailbox, controller: :settings
patch :view_settings_update, controller: :settings
patch :sharing_services_update, controller: :sharing_services
patch :actions_update, controller: :actions
end
end
And here's the settings route:
get :settings, to: 'settings#settings'
namespace :settings do
get :account
get :billing
get :import_export
get :feeds
get :help
post :update_credit_card
post :mark_favicon_complete
post :update_plan
post :font
post :font_increase
post :font_decrease
post :entry_width
end
It would depend on the attribute and what you're changing it to.
If it's a simple toggling of a boolean field, for example, you could get away with a link that fires off an ajax request, that makes the change and returns some HTML (eg. to update the link you clicked).
If it's something else, eg. a text field or a string or something, a form_for #user would be the typical way. But the form would only have one field, and the controller that processes the form post would have its strong parameters (or attr_accessible for Rails 3) set so that it will only accept data for that one field.
Was using the wrong route name. This was the right one:
create_mailbox_user_path
Rails 4 Use Strong Parameters to determine which attribute is allow to update like so:
def user_param
params.require(:user).permit(:attr)
end

Ruby on Rails: undefined method `' for #<#<Class:>>

I'm relatively new to rails and I've been struggling with this for a couple of days. I'd be much appreciated if you can see where I've gone wrong.
When I view the page in the web browser I get the following message:
Showing C:/Users/Matt/Documents/GitHub/Outputer/app/views/studies/index.html.erb where line #8 raised:
undefined method `studies_path' for #<#:0x6b03808>
8: <%= form_for #new_study do |f| %>
studies_controller:
def index
#line = current_user.lines.find_by_id(params[:line_id])
#machine = #line.machines.find_by_id(params[:machine_id])
#studies = #machine.studies.paginate(page: params[:page], :per_page => 10)
#new_study = #machine.studies.build
end
def create
#study = current_user.lines.machines.study.build(params[:study])
if #study.save
flash[:success] = "Study created"
else
flash[:error] = "Error : Invalid study description"
end
redirect_to :back
end
index.html
....
<section>
<%= form_for #new_study do |f| %>
<div class="field">
<%= f.text_field :description, placeholder: "New study description..." %>
</div>
<%= f.submit "Create", class: "btn" %>
<% end %>
</section>
....
Study Model
....
class Study < ActiveRecord::Base
belongs_to :machine
belongs_to :line
attr_accessible :avg_speed, :avg_uptime, :avg_yield, :description, :duration, :is_active, :start_time, :stop_time, :line_id
validates ....
has_many :events, dependent: :destroy
....
end
....
rake routes:
....
save_line_machine_study PUT /lines/:line_id/machines/:machine_id/studies/:id/save(.:format) studies#save {:has_many=>:machines}
line_machine_studies GET /lines/:line_id/machines/:machine_id/studies(.:format) studies#index {:has_many=>:machines}
POST /lines/:line_id/machines/:machine_id/studies(.:format) studies#create {:has_many=>:machines}
new_line_machine_study GET /lines/:line_id/machines/:machine_id/studies/new(.:format) studies#new {:has_many=>:machines}
edit_line_machine_study GET /lines/:line_id/machines/:machine_id/studies/:id/edit(.:format) studies#edit {:has_many=>:machines}
line_machine_study GET /lines/:line_id/machines/:machine_id/studies/:id(.:format) studies#show {:has_many=>:machines}
PUT /lines/:line_id/machines/:machine_id/studies/:id(.:format) studies#update {:has_many=>:machines}
DELETE /lines/:line_id/machines/:machine_id/studies/:id(.:format) studies#destroy {:has_many=>:machines}
....
routes.rb
resources :users
resources :lines, :has_many => :machines, only: [:index, :edit, :destroy, :show, :create] do
resources :machines, only: [:new, :create, :edit, :update] do
resources :studies
end
end
If I remove the form the page works fine which would suggest its in the form. I've tested the controller commands in the console and they all appear fine - I can create a new study object.
Thanks in anticipation
When you use form_for with a model instance, it defaults to the POST action for that controller which would be your studies_path. This is usually mapped to create in the controller.
From the looks of it, you need to add a route in routes.rb to handle that post request (see resources). You will also need a create method in your studies controller.
Here is a good guide for learning the basics of routing in rails.
Although a missing route is the most common reason for that (not-very-helpful) error, it can also be raised if one or both sides of a has_many/belongs_to relationship is missing or is incorrectly defined. Another place to look is a form field for an attribute that doesn't exist in the related model.
<%= form_for #new_study %> is equivalent to <%= form_for #new_study, url: studies_url %>. As your routes are defined differently, you need to pass the url you'd like to submit the form to to the url parameter (find form_for in the Rails API docs to see what other options it takes).
Three level deep nesting is kind of ugly to maintain, so I'd suggest the following:
resources :users
resources :lines do
resources :machines
end
resources :machines do
resources :studies
end
These shallow routes are much nicer to maintain. There's also a shallow: true option on nested resources calls, see the docs.
In your case:
# With the current setup
<%= form_for #new_study, url: line_machine_studies_path(#line, #machine)
# Same, my preference
<%= form_for [#line, #machine, #new_study] %>
# If you make your routes shallow,
# #line is not nescessary, as #machine holds all information about associations
<%= form_for #new_study, url: machine_studies_path(#machine) %>
# Same, my preference, what I would do
<%= form_for [#machine, #new_study] %>
General suggestions:
#study is preferred over #new_study. #study.new_record? will tell you whether the object is a new record if you need.
There's no has_many :... option on resources routes as far as I'm aware
Google rails shallow routes for more info. Keep nesting to two levels. Think about only what information you really require when creating objects and keep the URLs and url helpers as slim as possible.

How can I have multiple forms to update the same model?

I am new to Rails.
I have a User model. I would like a web page that allows users to change their :name and :email, and another web page that allows them to change their password.
Right now, I have a form to edit :name and :email at
/users/1/edit
The form on the page is
<%= form_for(#user) do |f| %>
My routes.rb has
resources :users
This works. Users can edit their :name and :email just fine. How do I now set up another web page with another form that allows them to change their password?
Thank you.
You can set up any actions you like in your controller. The default CRUD operations are there to cover the basics, but there is no inherent limitation to what you can do.
#controller:
def change_password
render :action => "change_password"
end
#routes:
map.resource :users, :member => {:change_password => :get}
#view:
<%= form_for(#user) do |f| %>
The above would create the route:
/users/1/change_password
In the view you simply have the change password fields => the form basically stays the same, submitting to your existing update action.
Per the comment from Sanjay regarding Toby's answer (and to save anyone the few minutes that I took to figure it out) in Rails 3 you would define the routes with:
#routes:
resources :users do
member do
get 'change_password'
end
end

Resources