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.
Related
I'm new to rails, and I've already learnt how to do CRUD using scaffold and using resource, I wanna know how to Do CRUD without using resource, However what I want to do is create custom methods for CRUD in the controller that will be like the traditional CRUD model. Please help me with this.
Actually, for the action index / new / show / create / edit / update / destroy, this is a convention in Ruby On Rails
If I'm right and if you're trying to change their name using resources in routes.rb (Changing by exemple the new action into def my_restaurant), Rails will render
Unknown action The action 'new' could not be found for
RestaurantsController
Netherless, you can create some methods to perform some particular action into the index, and add them in the "private section" you want to hide them to the public. There is no routes attach to this method.
class RestaurantsController < ApplicationController
def index
#restautants = Restaurant.all
#restaurants.sort_by_name
end
private
def sort_by_name
#some action here
end
end
If you want to create your own name method, you can personnalise it but you can't use resources "shortcut" in the routes.rb
by exemple :
#reviews_controller.rb
class ReviewsController < AplicationController
def index
#reviews = Reviews.all
end
def update
#review = Review.find(review_params[:id])
end
def history
#some action
end
private
def review_params
params.require(:review).permit(:liked, :comment, :id)
end
end
Then add a view
#app/views/reviews/history.html.erb
Don't forget the routes :
Rails.application.routes.draw do
resources :reviews, only: [:index, :update] do
collection do
get :history
end
end
end
I hope this will help you and complete the previous answer.
as for your second question :
I still do have one doubt tho..... Will <%= form_for #post do |f| %>
this form be enough for new_post and edit_post, will it automatically
identify them? If that's not enough can you please tell me the what
comes at new_post.html.erb and edit_post.html.erb....Thanks again for
the help.
If the form is the same for your new_post and edit_post, i may suggest you to put into a partial file. (For this example I used simple_form gem)
#app/views/posts/_form.html.erb
<%= simple_form_for(#post) do |f| %>
<%= f.input :name, label: "post name" %>
<%= f.input :photo, as: :file %>
<%= f.submit "Save", class:"btn btn-small btn-success" %>
<% end %>
and then render the partial in your views new file.
#app/views/posts/new.html.erb
<div>
<h1>New Post</h1>
</div>
<%= render "form" %>
Well I hope I could help you with this answer.
Do not hesitate too to read ruby documention. You may find more information that you're looking for too.
My answer may be redundant but it's the better way for me to clearly explain it...
In oder to use your own custom methods you need to create them in your controller, setup the route and if needed create an view.
# PostsController
def create_post
# Do your create stuff here
end
def read_post
# Do your read stuff here
end
def update_post
# Do your update stuff here
end
def delete_post
# Do your delete stuff here
end
# routes
post '/create_post', to: 'posts#create_post'
get '/read_post/:id', to: 'posts#read_post'
put '/update_post/:id', to: 'posts#update_post'
delete 'delete_post/:id', to: 'posts#delete_post'
With the controller and routes setup you will only need a view for the read_post method. The best way to do that is create the file: views/posts/read_post.html.erb
There is 7 CRUD routes to Create, Read, Update and Delete.
Rails.application.routes.draw do
get "restaurants", to: "restaurants#index"
get "restaurants/new", to: "restaurants#new", as: :new_restaurant
post "restaurants", to: "restaurants#create"
# NB: The `show` route needs to be *after* `new` route.
get "restaurants/:id", to: "restaurants#show", as: :restaurant
get "restaurants/:id/edit", to: "restaurants#edit", as: :edit_restaurant
patch "restaurants/:id", to: "restaurants#update"
delete "restaurants/:id", to: "restaurants#destroy"
end
So once the route create, you can create in the controller, the action that you need
class RestaurantsController < ApplicationController
def index
#restaurants = Restaurant.all
end
end
and the view
app/views/restaurants/index.html.erb.
The best practice is to create only the routes that you will need and to work in silos.
1 route, 1 controller action, 1 view.
I'm trying to call a mailer I have in the create action in my interests controller:
def create
if #interest.save
UserMailer.interest_to_seller(#interest).deliver_now
etc....
This works great when I'm going to the CRUD /interest/new form and creating it there, but I'm actually trying to call this from a post method in a different place. It's as if the create actions is being skipped all together, but that's impossible because the record is being created, right?
My routes.rb:
resources :seller_listings do
post :add_interest, on: :member
end
And in my view:
<%= link_to add_interest_seller_listings_path(m), method: :post do %>
Add Interest & Fire Email!
<%end%>
And in the interest controller, I have this action that that the post in the routes file calls:
def add_interest
current_user.mark_buyer_interest(seller_listing)
Which references this in the user.rb:
def mark_buyer_interest(listing)
buyer_interests.create(seller_listing: listing.id, accepted_by_seller: 0)
end
This works well, records are created, but the Mailer is skipped. Any suggestions? I've never done it this way before so any advice would be great. Thank you!
I think you should call UserMailer.interest_to_seller(#interest).deliver_now again in mark_buyer_interest.
def mark_buyer_interest(listing)
new_interest = buyer_interests.create(seller_listing: listing.id, accepted_by_seller: 0)
UserMailer.interest_to_seller(new_interest).deliver_now
end
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) %>
Forgive me if this is a very newby question.
how can i call a method (sharing an object on facebook) when user clicked on the share button in the view.
I can do the share/facebook parts, i just don't know how to call a method from the model when the user clicks on a button
my_controller.rb
def do_something
...
end
routes.rb
get "/something" => "my_controller#do_something", :as => :do_something
#you can also use post, put, delete or match instead of get
view
<%= link_to "call do something", do_something_path %>
for post etc...
<%= link_to "call do something", do_something_path, method: :post %>
I see two potential answers depending on your specific needs.
If you want to add a method to your model outside of the columns you've created for your object you can do so in the model.rb file:
model.rb
def name_twice
"#{self.name}#{self.name}"
end
You can then take an instance of a model to call this: "#model.name_twice".
If you want to add another routed method in the controller, you can define it in your models_controller file:
models_controller.rb
def approve
#model = Model.find_by_id(params[:model_id])
model.toggle!(:approved)
redirect_to #model
end
In order for the new controller function to work, you must add it in the routes file:
routes.rb
resources :models do
get 'approve', :on => :member
end
Hope this might be a little helpful. These examples should give you an idea of how to add other methods/actions to a model/controller.
I have this:
ActiveAdmin.register User do
controller do
def show
#user = User.find(params[:id])
show!
end
end
show do
attributes_table do
row "User" do
link_to #user.display_name, user_path(#user.slug)
end
end
end
end
But when I load the page, I get an error saying:
undefined method `display_name' for nil:NilClass
which means that #user is nil. I am positive that #user is set appropriately (meaning the finder is getting appropriate data that exists in the db). I'm thinking it has something to with how ActiveAdmin works that I'm unfamiliar with. Any thoughts?
Also, I know I could do show do |user|, but there are more complicated things I am using this for and need access to the user object in the controller.
Just in case someone else stumbles upon this:
controller.instance_variable_get(:#user)
should work as well.
There is controller in active admin, despite this you can not pass instance variable to arbre part. But you can use params hash for this:
ActiveAdmin.register User do
controller do
def show
params[:user] = User.find(params[:id])
show!
end
end
show do
attributes_table do
row "User" do
link_to params[:user].display_name, user_path(params[:user].slug)
end
end
end
end
P.S.: If you don't want to change params, then all instance variables are stored in #arbre_context.assigns. You may also do like:
link_to #arbre_context.assigns[:user].display_name, user_path(#arbre_context.assigns[:user].slug)
Instance variables are defined as helper methods. If you have that defined in your controller, you can access it. Alternatively, you can simply call resource, which will have reference to the active record object.
ActiveAdmin.register User do
controller do
def show
#user = User.find(params[:id])
show!
end
end
show do
attributes_table do
row "User" do
# note that your have access to `user` as a method.
link_to user.display_name, user_path(user.slug)
end
end
end
end
It seems does not work that way in activeadmin. The only instance variable available inside "form" block is #config.
The best way to solve this issue is to use partials as described in "Customizing the Form"
http://activeadmin.info/docs/5-forms.html
Not entirely sure how selects the correct instance variable on the model, but, you could give pretty much any name to the instance variable, i test some cases and it seems that just looks for the one that haves the same model type when you don't specify it, to answer your other question, you have many ways to do it
the simples one, just the same name as your instance variable,
in your case,
row :attr do
link_to user.display_name, admin_user_path(user)
end
the you have
row :attr do |any_name|
link_to any_name.display_name, admin_user_path(any_name)
end
and the last method i know, you have two escenarios, one for your active_admin files(.rb)
#eg: admin/user.rb
#arbre_context.assigns[:user]
or in custom .arb views, like a form for a custom collection_action(same but direct access)
assigns[:user]
eg:
#views/admin/users/new_invitation.html.arb(arbre) or html.erb
active_admin_form_for assigns[:user], :url => send_invitation_admin_users_path do |user|
....
end
form_for assigns[:user], :url => send_invitation_admin_users_path do |user|
....
end
semantic_form_for assigns[:user], :url => send_invitation_admin_users_path do |user|
.....
Like i say, i'm not sure how active_admin deals with instance variables, but a least you have multiple options, regards
If your goal is to set #user for the show action template, it's not necessary to do so, because active admin is already doing this for you.
If you use member_action, the #user object is there for you, it's called resource.
You can define a singleton method on your resource, it would be available in the view. This could make sense in some cases.
This is another way to pass information from the controller to the view.
member_action :show, method: :get do
resource.instance_eval do
define_singleton_method('language') do
'English'
end
end
end
show do
attributes_table do
row :name
row :email
row :id
row :language
end
end