This is something I am using inside my model
This is the URL that gets posted to another 3rd party website through API
Post Model (post.rb)
"#{content.truncate(200)}...more http://domain.com/post/#{id.to_s}"
The "id" is referring to the post id. How can I convert that into a random 8 digit alphanumeric?
Right now, it gets displayed as something that people can alter http://domain.com/post/902
I want http://domain.com/post/9sd98asj
I know I probably need to use something like SecureRandom.urlsafe_base64(8) but where and how can I set this up?
This is what I have in routes.rb
match '/post/:id', :to => 'posts#show', via: :get, as: :post
You only need to add one attribute to post. The attribute name is permalink.
Try running:
rails g migration add_permalink_to_posts permalink:string
rake db:migrate
You have twoActive Record Callbacks you can choose from: before_save or before_create (review the difference between both). This example is using the before_save callback.
note : for Rails 3.x
class Post < ActiveRecord::Base
attr_accessible :content, :permalink
before_save :make_it_permalink
def make_it_permalink
# this can create a permalink using a random 8-digit alphanumeric
self.permalink = SecureRandom.urlsafe_base64(8)
end
end
urlsafe_base64
And in your routes.rb file:
match "/post/:permalink" => 'posts#show', :as => "show_post"
In posts_controller.rb:
def index
#posts = Post.all
end
def show
#post = Post.find_by_permalink(params[:permalink])
end
Finally, here are the views (index.html.erb):
<% #posts.each do |post| %>
<p><%= truncate(post.content, :length => 300).html_safe %>
<br/><br/>
<%= link_to "Read More...", show_post_path(post.permalink) %></p>
<% end %>
"Altering the primary key in Rails to be a string" is related to your question.
I would:
Leave default ID on table
Not define resources routes, writing the needed ones with match (like match '/post/:code')
On controller#show, use Post.find_by_code(params[:code]).
Related
I need to capture a field added by a user in a form_for, inside the product show page.
My product.rb model as follows:
belongs_to :user
has_many :complaints
My complaint.rb model as follows:
belongs_to :product
belongs_to :user
My user.rb model as follows:
has_many :products
My product controller is a basic controller with all the new, create, edit, update actions and all the routes are good.
User looks at the product show page like this, and it's all good
http://localhost:3000/products/1
My goal is to create a complaint from the product show page, when user views the specific product. So I have created a complaints_controller.rb to capture all the details of the product, and create a complaint. I have an issue with capturing the complaint_number which is a field inside the complaints table.
Here is my form inside the product show page
<%= form_for([#product, #product.complaints.new]) do |f| %>
<%= f.number_field :complaint_number, placeholder: "Enter complaint number you were given" %>
<%= f.submit 'Complaint' %>
<% end %>
Here is my complaints_controller.rb
Goal is to capture the complaint_number fields and run the make_complaint method to create a complaint and populate rest of the fields in the newly created row of the complains table.
class ComplaintsController < ApplicationController
before_action :authenticate_user!
def create
# Will Get product_id from the action in the form in product show page.
product = Product.find(params[:product_id])
# This complaint_number does not seem to work
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
# Now I want to run a make_complaint method and pass the product and the complaint number. This fails, I can't capture the complaint_number in the form from user input.
make_complaint(product, complaint_number)
redirect_to request.referrer
end
private
def make_complaint(product, complaint_number)
complaint = product.complaints.new
complaint.title = product.title
complaint.owner_name = product.user.name
complaint.owner_id = product.user.id
# Note: complaint_number and current_complaint are a fields in the Orders table
# Note:
complaint.current_complaint = complaint_number
if complaint.save
flash[:notice] = "Your complaint has been sent!"
else
flash[:alert] = complaint.errors.full_messages
end
end
end
For routes I have added resources :complaint, only: [:create] inside the resources of products to get products/:id/complaints
My routes.rb is like this
Rails.application.routes.draw do
get 'products/new'
get 'products/create'
get 'products/edit'
get 'products/update'
get 'products/show'
root 'pages#home'
get '/users/:id', to: 'users#show'
post '/users/edit', to: 'users#update'
resources :products do
member do
delete :remove_image
post :upload_image
end
resources :complaint, only: [:create]
end
devise_for :users, path: '', path_names: { sign_in: 'login', sign_up: 'register', sign_out: 'logout', edit: 'profile' }
Your form has complaint_quantity:
<%= form_for([#product, #product.complaints.new]) do |f| %>
<%= f.number_field :complaint_quantity, placeholder: "Enter complaint number you were given" %>
<%= f.submit 'Complaint' %>
<% end %>
Your controller has complaint_number:
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
If you check your params from the server log, I bet you'll see the value you are looking for is coming across as complaint_quantity and not complaint_number.
UPDATE
With the form misspelling corrected, the error persists, so let's check into more areas:
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
So, break that down:
1. What does params actually include?
Is :complaint_number being submitted from the form?
If not, the form still has an error somewhere.
2. Does product.complaints actually include a complaint that could be matched by complaint_number?
I don't know your data structure well enough to tell, but it looks to me like you might actually want to do:
Complaint.find_by(complaint_number: params[:complaint_number])
instead of:
products.complaints.find_by(complaint_number: params[:complaint_number])
UPDATE #2
You know the problem is with your params.
I'm confident you aren't accessing your params correctly since you are using a nested form:
form_for([#product, #product.complaints.new])
Should mean your params are structured like { product: { complaint: { complaint_number: 1234 }}}
So params[: complaint_number] is nil because it should really be something like params[:product][:complaint][:complaint_number]
Please look at your server log in your terminal right after you submit the form to see the structure of your params. Or insert a debugger in the controller action and see what params returns.
ALSO, Instead of accessing params directly, you should whitelist params as a private method in your controller.
Something along these lines:
private
def product_complaint_params
params.require(:product).permit(:id, complaint_params: [ :complaint_number ])
end
See this: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html
My rails application default url for post is _http://0.0.0.0:3000/posts/7, how can i display url like this _http://0.0.0.0:3000/posts/cer4235235ft5435rerjk343f with numbers and text.
Create another column in the database, update it with some alpha numeric string, and use that column for querying.
eg:
Add a column permalink to your posts table
class Post < ActiveRecord::Base
before_create :create_permalink
def create_permalink
self.permalink = SecureRandom.hex
end
..
Change links to use this String
<%= link_to post.title, post_path(:id => post.permalink) %>
Modify your controller
def show
#post = Post.where(:permalink => params[:id]).first
end
So I have an email field in a model.
I would like a path in this model, where a field shows up in the view, and when I type an email address, and that address matches an existing model, it gets deleted. For unsubscribing from newsletter.
something like this:
newsletter_controller.rb
def unsubscribe(email)
#newsletter = Newsletter.where(:email => email)
#newsletter.destroy
end
in the view:
simple_form_for #newsletter do |f|
f.input :email, method: delete
end
I got no idea how the view should work in the Rails Way.
In config/routes.rb, I suppose you have resources :newsletters
Add a route for unsubscribe as following:
resources :newsletters do
post 'unsubscribe', :on => :member
end
Check rake routes, you should have obtained a route path as unsubscribe_newsletter_path with POST verb.
Now, in your view:
=form_for(:newsletter, :url => unsubscribe_newsletter_path) do |f|
=f.label :email
=f.text_field :email
=f.submit "Unsubscribe"
(Change it as per syntax of simple_form)
Now, in newsletter_controller.rb(it should have been newsletter*s*_controller), add the method as:
def unsubscribe
#newsletter = Newsletter.where(:email => params[:newsletter][:email])
#newsletter.destroy if #newsletter
redirect_to root_path
end
I hope it helps. Comment with places where it gives you error or doesn't work. I'll try to help.
Good luck. :)
I'm using Ruby on Rails 2.3.8 and permalink-fu plugin. I would like to know how to generate permalinks like this: /posts/44444/this-is-the-title instead of /posts/44444-this-is-the-title
I've tried modifying my Post model as follows:
has_permalink :title, :update => true
def to_param
"#{permalink}"
end
And my routes file as follows:
map.show "/posts/:id/:permalink", :controller => 'posts', :action => 'show'
Then, if I manually type the url with that format, it will work, but if I make a link out of a post in my view as follows, it wont generate the link formatted that way:
<%= link_to p.title, p %>
Where p represents a post.
How can I do so when I call a post like that, I get a permalink formatted as /posts/:id/:permalink instead of /posts/:id-:permalink?
Try this one...
on model:
def to_params
[self.id, self.permalink]
end
on views:
<%= link_to p.title, show_path(p) %>
I am trying to get my urls to look like this:
example.com/posts/id_of_post/title_of_post
I have this in my controller:
match ':controller/:id/:link', :controller => 'posts', :action => 'show'
Say I have a list of posts.. how can I link to them?
<%= link_to 'Show', post %>
Just gives the usual /posts/id
On another note, at the minute I am making a url-friendly link when a post is created and storing it in the database. Would it be better to create on the fly? Is that possible/better?
I saw this in an answer to another question:
def to_param
normalized_name = title.gsub(' ', '-').gsub(/[^a-zA-Z0-9\_\-\.]/, '')
"#{self.id}-#{normalized_name}"
end
That would work if I could change the - to a /. Possible?
I recommend just doing this instead of the gsub stuff:
def to_param
"#{self.id}-#{title.parameterize}"
end
Downside is that if the title changes, the URL changes. Which is a downer.
So a lot of implementations will do
before_create :permanize
def permanize
permalink = title.parameterize
end
def to_param
"#{self.id}-#{permalink}"
end
This is what I did:
I added this to my post#create:
#post.link = (#post.title.parameterize)
I will give the user the option to edit the title for up to 5 mins after posting.
My route:
match "/posts/:id/:link" => "posts#show", :as => "story"
and my index view for posts
<%= link_to 'Show', story_url(post, post.link) %>