Rails use object property in path - ruby-on-rails

I'm trying to get a path helper to work with a object's name property
so in routes
get 'reset/:name', to: 'users#reset_name', as: 'reset_name'
in controller reset_name def
... #user
in view (html.erb)
... <%= link_to reset_name_path(#user) %>
for some reason the link is still coming out like /reset/name/1, which uses the object's id and not name. What am I doing wrong?

You can override the to_param method to make Rails use the name instead of the id when generating routes. This will apply to all routes that operate on the User model.
class User < ActiveRecord::Base
def to_param
name
end
end
Alternatively, if you only want to do this for some routes, you can just change your link_to:
<%= link_to reset_name_path(:name => #user.name) %>

Related

Rails, how to change automatic generated link

I have a RoR app. And in app users can create posts. I've connected Posts table in my routes.rb via resources :posts. And right now - link to created post look like: http://mysitename.com/posts/1 (where 1 is post number).
What i want to do, is to make rails generate link to post. So users didn't see how much posts I have in my DB. And as result it must look like http://mysitename.com/post/generatedlink. It must generate, for example post theme.
For start, we must create link column in Posts table. And make it to generate something like that:
#post.link = #post.theme.parameterize.underscore
But I don't understand, where to put this code.
And the next problem is: "How to replace post/1 for #post.link?"
Hope, I make my self clear. If you'll say I can provide information, what is needed to resolve my question.
UPDATE
What I did after #SteveTurczyn advise.
I've created new column, called random_link as a string.
I didn't touch my routes.rb:
resources :posts
My post.rb (post model) look like this:
after_validation :add_link
def add_link
self.random_link = self.theme.to_slug_param
# to_slug_param it's a gem for translating from other language into english
end
def to_param
random_link
end
I don't have find method. My posts_controller.rb look like this:
def show
#post = Post.find_by_random_link(params[:id])
right_menu_posts
random_link_to_other_post(#post)
end
private
def random_link_to_other_post(post)
random_post = Post.where.not(id: post.id)
#random_post = random_post.sort_by {rand}.first
end
def right_menu_posts
#posts_for_video_in_right_menu = Post.where(video: true)
end
And html.erb:
<%= #post.theme %>
<%= #post.content %>
<% for post in #random_post %>
<%= link_to post %>
<% end %>
<% for post in #posts_for_video_in_right_menu %>
<%= link_to post %>
<% end %>
And on a main page (where i have a list of posts) a keep getting an error: NoMethodError in Home#index private method 'to_param' called for #<Post:0x007fae3096bf78>.
The technique is referred to as slugifying and you need to do three things...
(1) create a new field called slug in your posts table.
(2) add this code to your Post model...
after_validation :generate_slug
private
def generate_slug
self.slug = theme.parameterize.underscore
end
public
def to_param
slug
end
(3) finally, in your controllers where you have find_post methods, rewrite it to be...
def find_post
Post.find_by_slug(params[:id])
end
The to_param method in the model is how things like post_path(#post) build the url... the to_param if not replaced substituted the id field but by writing your own to_param method you can ensure that the slug field is substituted instead.
Ensure that 'to_param' is a public method! Don't put it in the private part of your model. You can do that by putting public immediately before the to_param method. You should then put private after the method definition if subsequent methods are to be private.

name error uninitialized constant path in rails

I'm trying to make a form that will post to a database, I'm really struggling at the moment and i'm getting this error.
NameError in AddController#index
uninitialized constant AddController::Newevents
Could you advise what i would need to do?
Heres all the code i have
Form
<%= simple_form_for(#newevent) do |f| %>
<%= f.input :eventname, required: true %>
<%= f.input :eventdate %>
<%= f.input :eventimage %>
<%= f.button :submit %>
<% end %>
controller
class AddController < ApplicationController
def index
#newevent = Newevent.new
end
end
Model
class Newevent < ActiveRecord::Base
def event_params
params.require(:Newevent).permit(:eventname, :eventdate, :eventimage)
end
end
Routes
resources :add
Edit
i now have this error undefined methodnewevents_path'` after changing this
#newevents = Newevent.new
It seems that you miscopied your code here. The error message indicates that your index method actually looks like this
def index
#newevent = Newevents.new
end
Remove the s from the end of Newevent and it should work.
RE: your edit
Your routes declare that you have a resource named add, if you want to show and create your Newevent objects, then you should create a controller for that. Declare resources :newevents in your routes and create a controller to handle it.
You should research RESTful routes, because that's what Rails's resource routing works best with. The form to create a new object should be displayed by the new action and not index.
You should be using create method instead of index if you are using POST http method. index will be called if you are using GET method and it shouldn't be used to post the form data. Refer this link for more information on rails routing.
class AddController < ApplicationController
def create
#newevent = Newevent.new
end
end

How to pass params to new view in Ruby on Rails app?

I'm trying to make simple app. I input my first name and last name to simple <%= form_for #data do |f| %> rails form and after submitting it, app should render simple text like this. My first name is <%= data.first_name %> and my last name is <%= data.last_name %>. I don't know why but my app is saying this error:
undefined local variable or method `data' for
It's probably saying it because no params are passed to view.
Here is my code.
routes.rb
resources :data, only: [:new, :create, :index]
data_controller.rb
class DataController < ApplicationController
def new
#data = Data.new
end
def index
end
def create
#data = Data.new(data_params)
if #data.valid?
redirect_to #data
else
render :new
end
end
private
def data_params
params.require(:data).permit(:first_name, :second_name)
end
end
/views/data/new.html.erb
<%= form_for #data do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :second_name %>
<%= f.text_field :second_name %>
<%= f.submit 'Continue', class: 'button' %>
<% end %>
/views/data/index.html.erb
<h2>Coolest app ever :D</h2>
<p>My first name is: <%= data.first_name %>.</p>
<p>And my second name is: <%= data.second_name %>.</p>
/models/data.rb
class Data
include ActiveModel::Model
attr_accessor :first_name, :second_name
validates :first_name, :second_name, presence: true
end
Please help to find out why params are not passing to next page. Thanks anyways :D
Your view should look like this:
<h2>Coolest app ever :D</h2>
<p>My first name is: <%= #data.first_name %>.</p>
<p>And my second name is: <%= #data.second_name %>.</p>
Also, I would suggest that calling a model something generic like Data is not a very Rails-y approach. Generally, domain models correspond to real-world things like User and Article, which are easy to understand and relate to. It'll get confusing quite fast if you use need to make another model and want to call it Data2 or something :)
Edit:
Since you specified that you do not wish to use the database, I would recommend passing in the object params through the redirect:
redirect_to(data_path(data: #data))
and in your controller's index method:
def index
#data = Data.new(params[:data])
end
Now your view should render properly, since you're passing the in-memory #data object attributes as params within the redirect. You then recreate this object in the index page or wherever you wish to redirect to.
To expand on Matt's answer, the reason you're getting NilClass errors is because:
You're redirecting to a data#show action when no show action has been enabled within your routes file. Since you've set your views up for the index, I'm assuming you want to redirect there when the #data object has been verified as valid:
redirect_to data_path
However I would recommend you follow Rails conventions and specify the data#show route within your routes.rb:
resources :data, only: [:index, :new, :create, :show]
and in your data_controller.rb:
def show
#data = Data.find(params[:id])
end
Another problem is that you're not actually saving the #data object upon creating it. The new method populates the attributes, and valid? runs all the validations within the specified context of your defined model and returns true if no errors are found, false otherwise. You want to do something like:
def create
#data = Data.new(data_params)
if #data.save
redirect_to data_path
else
render :new
end
end
Using save attempts to save the record to the database, and runs a validation check anyways - if validation fails the save command will return false, the record will not be saved, and the new template will be re-rendered. If it is saved properly, the controller will redirect to the index page, where you can call upon the particular data object you want and display it within your view.

undefined method `_path' in form, with routes defined at :resources

I've been stuck on this for a bit and can't figure out the exact reason why I'm getting the following error:
undefined method `entries_path' for <%= form_for(#entry) do |f| %>
entry_controller:
class EntryController < ApplicationController
def index
end
def new
#entry = Entry.new
end
def create
#entry = Entry.new(user_params)
if #entry.save
redirect_to #entry
else
render 'new'
end
end
private
def user_params
params.require(:entry).permit(:comment, :flag)
end
end
routes has:
resources :entry
and the new page where the error occurs:
<%= form_for(#entry) do |f| %>
<%= f.label :comment %>
<%= f.text_field :comment %>
<%= f.label :flag %>
<%= f.text_field :flag %>
<% end %>
I can't figure out why I'm getting this error.
form_for needs to reference the path associated with #entry (i.e. entries_path), but your routes.rb file uses the singular form of the resource (:entry) rather than the required plural form (:entries), so the proper path names don't exist.
Rails models use the singular form, but the Rails database, controllers, views use the plural form and this is reflected in the routes file. One way to remember this is that a model is describing a single class that each object belongs to. Everything else, pretty much, is responsible for managing multiple instances, so while they themselves are singular (e.g. Controller), they refer to the objects they manage in the plural form (e.g. EntriesController, controller/entries directory).
See Ruby on Rails plural (controller) and singular (model) convention - explanation for more discussion of this.
Controller and views should always be treated in plural form. For example, if you have an object Book, then the controller declaration should be
class BooksController < ApplicationController
and the views( new, edit, show, index ) should be inside a folder named
/books
Also, the declaration of routes should be in plural form. In this case, the routes should be declared as
resources :books
You could try to generate the controller and view folder by running in your terminal:
rails generate controller name_of_object_in_plural_form( for sample, books)
The script will generate a controller named books_controller.rb and /books folder under /views

How to make the normal way of Routes with hash keys

Now I can see my received message by accessing to example.com/messages/46747
and My routes are set set like this
'messages/:id' => 'messages#show', :as => 'show_messages'
The link tag to access that page is set like
<%= link_to 'show message', show_messages_path %>
In this case, the IDs of the messages are shown in the URL.
I guess that IDs should be taken place by hash keys in most cases.
How can I achieve that?
Use ActiveRecord::Base to_param in order to change the url generated by the path:
class User < ActiveRecord::Base
def to_param # overridden
name
end
end
user = User.find_by_name('Phusion')
user_path(user) # => "/users/Phusion"
Just override the to_param method in Message class by the text you'd like to see in the url.

Resources