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
Related
I want to have two input fields, but only one column in the database.
The first input is stored data in numbers and the other one is stored data in numbers divided by 24.
You can put data only in one field.
Is there any possible way to do this?
UPD:
Migration:
def change
add_column :employees, :hourly_payment , :integer
end
View:
employees/_form.html.erb
<%= simple_form_for #employee do |form| %>
<%= form.input :name %>
<%= form.input :hourly_payment %>
<%= form.input :monthly_payment %>
<%= form.button :submit, class: "btn btn-success" %>
<% end %>
Your model and database tables are the internals of your application and are not actually tied to the view by anything except how easy ActiveRecord makes it to use convention over configuration to link the two*.
In Rails style MVC the controller is responsible for passing user input to the model. Usually you would just do this with simple mass assignment:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
# ...
end
private
def user_params
params.require(:user)
.permit(:email, :salary)
end
end
This is basically just passing a whitelisted hash of parameters straight to the model as is and it all gets passed to the setters that ActiveRecord magically created for you by reading the database schema.
But there is nothing stopping you from assigning attributes manually:
class UsersController < ApplicationController
def create
#user = User.new(user_params) do |user|
user.salary = calculated_salary
end
# ...
end
private
def user_params
params.require(:user)
.permit(:email)
end
def calculated_salary
if params[:user][:hourly_payment].present?
params[:user][:hourly_payment]
elsif params[:user][:monthly_payment].present?
params[:user][:monthly_payment].to_i / 168
else
0 # sorry no cookies for you
end
end
end
Or monkeying with the parameters object:
def user_params
params.require(:user)
.permit(:email)
.merge(salary: calculated_salary)
end
It is after all just a hash on steroids. The only thing that Rails will prevent you from is passing a parameters object that has not been whitelisted.
There is no stone tablet for what you can do in a controller. The only thing to bear in mind is that controllers are notoriously hard to test and fat controllers are a recipe for disaster.
If you're doing anything more complicated there are better solutions such as form objects, decorators or service objects.
You'll need to create a view for that. Here is an example of migration:
def up
sql = %(CREATE OR REPLACE VIEW my_view_models AS
SELECT m.*,m.int_field*12 as my_new_value from my_models m
)
self.connection.execute(sql)
end
def down
self.connection.execute('DROP VIEW IF EXISTS my_view_models')
end
Then, you can access your value with method my_new_value on your model. You'll need to change the name of the default table matching your model.
class MyModel
self.table_name = 'my_view_models'
end
And access it via
MyModel.first.my_new_value
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.
I am new to rails developement and to the MVC architecture. I have a little application where I can add Videos' URLs from Dailymotion or Youtube and get the tweets related to that URL using the twitter gem in Ruby on Rails.
Now i'm able to store the tweets like this : (This is the video controller)
def show
#video = Video.find(params[:id])
# Creating a URL variable
url = #video.url
# Search tweets for the given video/url
#search = get_client.search("#{#video.url} -rt")
# Save tweets in database
#search.collect do |t|
tweet = Tweet.create do |u|
u.from_user = t.user.screen_name.to_s
u.from_user_id_str = t.id.to_s
u.profile_image_url = t.user.profile_image_url.to_s
u.text = t.text.to_s
u.twitter_created_at = t.created_at.to_s
end
end
I'm not sure if this is the right way to do it (doing it in the controller ?), and what I want to do now is to specify that those tweets that have just been stored belong to the current video. Also I would like to have some sort of validation that makes the controller look in the database before doing this to only save the new tweets. Can someone help me with that ?
My models :
class Video < ActiveRecord::Base
attr_accessible :url
has_many :tweets
end
class Tweet < ActiveRecord::Base
belongs_to :video
end
My routes.rb
resources :videos do
resources :tweets
end
This is an example of a "fat controller", an antipattern in any MVC architecture (here's a good read on the topic).
Have you considered introducing a few new objects to encapsulate this behavior? For example, I might do something like this:
# app/models/twitter_search.rb
class TwitterSearch
def initialize(url)
#url = url
end
def results
get_client.search("#{#url} -rt")
end
end
# app/models/twitter_persistence.rb
class TwitterPersistence
def self.persist(results)
results.map do |result|
self.new(result).persist
end
end
def initialize(result)
#result = result
end
def persist
Tweet.find_or_create_by(remote_id: id) do |tweet|
tweet.from_user = screen_name
tweet.from_user_id_str = from_user_id
tweet.profile_image_url = profile_image_url
tweet.text = text
tweet.twitter_created_at = created_at
end
end
private
attr_reader :result
delegate :screen_name, :profile_image_url, to: :user
delegate :id, :user, :from_user_id, :text, :created_at, to: :result
end
Notice the use of find_or_create_by ... Twitter results should have a unique identifier that you can use to guarantee that you don't create duplicates. This means you'll need a remote_id or something on your tweets table, and of course I just guessed at the attribute name (id) that the service you're using will return.
Then, in your controller:
# app/controllers/videos_controller.rb
class VideosController < ApplicationController
def show
#tweets = TwitterPersistence.persist(search.results)
end
private
def search
#search ||= TwitterSearch.new(video.url)
end
def video
#video ||= Video.find(params[:id])
end
end
Also note that I've removed calls to to_s ... ActiveRecord should automatically convert attributes to the correct types before saving them to the database.
Hope this helps!
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]).
When user's create a post I'd like to set the user_id attribute first. I'm trying to do this using alias_method_chain on the arrtibutes method. But I'm not sure if this is right as the problem I thought this would fix is still occurring. Is this correct?
Edit:
When my users create a post they assign 'artist(s)' to belong to each post, using a virtual attribute called 'artist_tokens'. I store the relationships in an artist model and a joined table of artist_ids and post_ids called artisanships.
I'd like to to also store the user_id of whomever created the artist that belongs to their post (and I want it inside the artist model itself), so I have a user_id column on the artist model.
The problem is when I create the artist for each post and try to insert the user_id of the post creator, the user_id keeps showing as NULL. Which is highly likely because the post's user_id attribute hasn't been set yet.
I figured to get around this I needed to set the user_id attribute of the post first, then let the rest of the attributes be set as they normally are. This is where I found alias_method_chain.
post.rb
attr_reader :artist_tokens
def artist_tokens=(ids)
ids.gsub!(/CREATE_(.+?)_END/) do
Artist.create!(:name => $1, :user_id => self.user_id).id
end
self.artist_ids = ids.split(",")
end
def attributes_with_user_id_first=(attributes = {})
if attributes.include?(:user_id)
self.user_id = attributes.delete(:user_id)
end
self.attributes_without_user_id_first = attributes
end
alias_method_chain :attributes=, :user_id_first
EDIT:
class ArtistsController < ApplicationController
def index
#artists = Artist.where("name like ?", "%#{params[:q]}%")
results = #artists.map(&:attributes)
results << {:name => "Add: #{params[:q]}", :id => "CREATE_#{params[:q]}_END"}
respond_to do |format|
format.html
format.json { render :json => results }
end
end
In your controller, why not just do this:
def create
#post = Post.new :user_id => params[:post][:user_id]
#post.update_attributes params[:post]
...
end
But it seems to me that it would be much better to create the artist records after you've done validation on the post rather than when you first assign the attribute.
EDIT
I would change this to a callback like this:
class Post < ActiveRecord::Base
attr_accessor :author_tokens
def artist_tokens=(tokens)
#artist_tokens = tokens.split(',')
end
after_save :create_artists
def create_artists
#artist_tokens.each do |token|
...
end
end
end