Creating blog archive in rails - ruby-on-rails

Controller:
class PostsController < ApplicationController
def index
#posts = Post.published
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
def show
.
.
end
def month
#posts_by_month = Post.find(:all, :order => "created_at DESC").group_by { |post| post.created_at.strftime("%B %Y") }
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
end
posts#month View:
<% #posts_by_month.each do |monthname, posts| %>
<p><%= monthname %></p>
<div>
<ul>
<% posts.each do |post| %>
<li><p><%= post.title %></p></li>
<% end %>
</ul>
</div>
<% end %>
posts#index view:
<h1>Listing posts</h1>
<%= render :partial => #posts %>
<h2>Blog archive</h2>
<%= ?I want link to single months archive here? %>
I'm creating a blog in rails and I thought I would add an archive section that you commonly see in the sidebar of many blogs. When I navigate to the posts#month view it displays the month as a heading and lists all the posts made during that month.
What I want to do now is have a list of months that posts where made on the posts#index view with each month linked to the posts#month view described above.
I'm not sure what to put on the posts#index view to accomplish this. Any ideas on what to put there or a better way to implement this would be great.
Any help appreciated!

I am always do it by this way for grouping:
#posts_by_month = Post.find(:all, :order => "created_at DESC").group_by { |post| post.created_at.beginning_of_month }
Then create for example posts/_post_archive.html.erb:
<div id="post-archive">
<% #posts_by_month.each do |month, posts| %>
<h4><%= "#{month.strftime('%B %Y')} (#{posts.count}):" %></h4>
<ul>
<% for post in posts %>
<li><%= link_to post.title, post %></li>
<% end %>
</ul>
<% end %>
</div>
And where it will be needed write this <%= render :partial => "posts/post_archive" %>
UPDATE:
In your controller create action:
def by_year_and_month
#posts = Post.where("YEAR(created_at) = ? AND MONTH(created_at) = ? ", params[:year], params[:month]).order("created_at DESC")
end
In your routes.rb:
match 'posts/by_year_and_month/:year/:month' => 'posts#by_year_and_month', :as=> :posts_by_year_and_month
And modifying our posts/_posts_archive.html.erb:
<h4><%= link_to "#{month.strftime('%B %Y')} (#{posts.count}):", posts_by_year_and_month_path(:year=> month.year, :month => month.month) %></h4>

In case someone try this for Postgres, the above method will not work.
def by_year_and_month
#posts = Post.where("YEAR(created_at) = ? AND MONTH(created_at) = ? ", params[:year], params[:month]).order("created_at DESC")
end
so you can do it like this,
def by_year_and_month
#posts = Post.where('extract(year from created_at) = ?', params[:year]).where('extract(month from created_at) = ?', params[:month]).order("created_at DESC")
end
Hope this will help someone.

In PostsController
def index
#posts = Post.published
#grouped_posts = Post.select("COUNT( * ) AS posts, MONTHNAME( created_at ) AS MONTH , YEAR( created_at ) AS YEAR").group("MONTH, YEAR").order("YEAR, MONTH( created_at )")
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
and then in view index.html.erb
<h1>Listing posts</h1>
<%grouped_posts.each do |rec|%>
<%=link_to "#{rec.month} #{rec.year} (#{rec.posts})", {:controller => "posts", :action => "month", :month => rec.month, :year => rec.year} %>
<%end%>
<%= render :partial => #posts %>
<h2>Blog archive</h2>
<%= ?I want link to single months archive here? %>
And then change the month action in posts_controller to accept two parameters params[:month] & params[:year]
Hope this will help

Related

Designing a Search bar and retrieving data from database rails4

Now i am tried by following to this article http://www.rymcmahon.com/articles/2
My product.rb file is
class Product < ActiveRecord::Base
def self.search(search)
where("name LIKE ?","%#{search}%")
end
end
my index file is
Data
<%= form_tag(products_path, :method => "get") do %>
<%= text_field_tag :search, params[:search], placeholder: "products" %>
<%= submit_tag "Search", :name => nil %>
<% end %>
my index method is
def index
#product = Product.all
if params[:search]
#recipes = Product.search(params[:search]).order("created_at DESC")
else
#recipes =Product.all.order("created_at DESC")
end
end
But it didn't filtering the data
my output is:
dinesh 200 2016-09-21 06:10:40 UTC
reddyc 300 2016-09-21 06:16:31 UTC
Goods 200 2016-09-21 09:33:56 UTC
That article doesn't seem to be entirely correct, because the first collection assignment seems redundant. I'd write it like this:
def index
#products = search_products.order(created_at: :desc)
end
private
def search_products
if params[:search]
Product.search(params[:search])
else
Product.all
end
end
Then in your views you use #products variable instead of #recipes. It should work
You have taken two different variables for product, also #product = Product.all is not required, as your taking the product variable again in both if and else cases.
def index
if params[:search].present?
#products = Product.search(params[:search]).order("created_at DESC") //I recommend taking plural(products)
else
#products = Product.all.order("created_at DESC")
end
end
Your index.html.erb can be,
<% #products.each do |product| %>
<tr>
<td><%= product.field1 %></td>
<td><%= product.field2 %></td>
<td><%= product.field3 %></td>
</tr>
<% end %>
My controller is:
def show
#product = Product.find(params[:id])
end
def search
end
def searchable
searchword = params[:search]
#data = Product.where(name: searchword)
end
and my searchable.html.erb is:
<h1>searching for '<%= params[:search] %>'</h1>
<% if #data.empty? %>
<h1>no results found</h1>
<% end %>
<% #data.each do |s| %>
<%= s.name %>
<%= s.created_at %><br>
<% end %>
And search.html.erb
<%= form_tag searchable_path, method: 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", name: nil %>
<% end %>
And my routes are assigned as
get 'search' => 'products#search'
get 'searchable' => 'products#searchable'
By this I am able to search data in my database.And it is working fine.

Tag index in rails: adding a second model with act as taggable

Can't seem to find an answer for my specific problem.
I have a rails 4 application which displays both a blog (model is post) and a portfolio (model is pletool). My post model is working fine, as is my pletool model except that I am unable to replicate my index controller from posts to pletools.
I'm using act as taggable to tag and have two separate tag types - "tags" for the post and "pletags" for the pletools.
For both index views I want to be able to display a tag cloud of the most popular tags, which if the user then clicks on will filter to only posts or pletools of that particular tag.
I get an 'undefined method nil class' error when I try to alter the index definition in my pletools_controller.rb from "tags" to "pletags".
pletools_controller.rb
class PletoolsController < ApplicationController
layout 'application'
before_action :find_pletool, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :tag_cloud
def index
if params[:pletag].present?
#pletool = Pletool.tagged_with(params[:pletag]).paginate(:page => params[:page], :per_page => 20)
else
#pletools = Pletool.all.order('created_at DESC').paginate(page: params[:page], per_page: 20)
end
end
def tag_cloud
#pletags = Pletool.tag_counts_on(:pletags, :limit => 10, :order => "count desc")
end
def new
#pletool = Pletool.new
end
def show
end
def edit
end
def create
#pletool = Pletool.new(pletool_params)
if #pletool.save
redirect_to #pletool, notice: "Tool succesfully saved!"
else
render 'new', notice: "Try Again. I was unable to save your PLE Tool."
end
end
def update
if #pletool.update(params[:pletool].permit(:title, :description, :link, :image, :pletag_list))
redirect_to #pletool, notice: "PLE Tool succesfully edited!"
else
render 'edit'
end
end
def destroy
#pletool.destroy
respond_to do |format|
format.html { redirect_to pletools_url, notice: 'Ple Tool was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def pletool_params
params.require(:pletool).permit(:title, :link, :description,:image, :image_url, :slug, :pletag, :pletag_list)
end
def find_pletool
#pletool = Pletool.friendly.find(params[:id])
end
end
index.html.erb
<% #pletools.each do |pletool| %>
<li class="post msnry-blog-entry #">
<% if pletool.image.exists? %>
<div class="click-slide click-top">
<%= link_to image_tag pletool.image.url(:large) %>
<div class = "text-center">
<%= link_to '<span class = "ti ti-eye"></span>'.html_safe, pletool %>
</div>
</div>
<% end %>
<div class="msnry-blog-entry-text">
<h4 class"h5"><%= link_to pletool.title, pletool %></h4>
<p><%= markdown truncate(pletool.description, :length => 150) %></p>
<ul class="msnry-blog-entry-meta list-inline">
<li>
<span class="ti ti-pencil-alt">
</span>
by Anthony Llewellyn
</li>
<li>
<span class="ti ti-calendar">
</span>
<%= pletool.created_at.strftime('%A, %B %d') %>
</li>
<li class="entry-tags hidden-xs">
<% pletool.pletags.any? %>
<% pletool.pletags.each do |tag| %>
<li><a href="#">
<%= link_to tag.name, pletag_path(tag.name) %>
</a></li>
<% end %>
</li>
</ul>
</div>
</li>
<% end %>
</li>
</ul>
ok after much searching (with no real answers that I could find online) and playing around with various parameters, it turns out i had some very minor issues in a couple of my scripts.
firstly for pletools_controller.rb
i needed to change #pletool to plural in a couple of lines e.g.
#pletools = Pletool.tagged_with(params[:pletag]).paginate(:page => params[:page], :per_page => 20)
also in my routes.rg i have configured two separate index routes and again i needed to make a minor change from get 'pletag:tag' to get 'pletag:pletag'
here is the relevant section of my final routes.rb
get 'pletags/:pletag', to: 'pletools#index', as: :pletag, layout: 'application'
get 'tags/:tag', to: 'posts#index', as: :tag, layout: 'application'
Hope that this helps someone else out one day / some day!

How can I implement fragment cache with dalli when using pagination (kaminari)?

I know dalli (caching) is pretty powerful plugin to enhance performance for static pages.
But what about dynamic pages with pagination, which are updated quite often?
What is the correct way to set up dalli?
One problem I've encountered for example: dalli recognizes different params[:page] as the same page when using pagination:(
How would you guys design the system when using dalli for both
the page that gets updated so often
the page that won't be updated so often
For example. I have 4 models such as User, Community, Topic, and Comment(defined Polymorphic to both Community, and Topic. They can be created or shown in #show pages)
It's like
Community > (has_many) > Topics > (has_many) > Comments
Community > (has_many) > Comments
All of them belongs to user(creator)
My current codes are just like this(This is quite bit long. Sorry about that)
I'm facing the pagination problem when using caching...
-------------------------------------Settings----------------------------------------
config/environments/development.rb
config.consider_all_requests_local = true
config.action_controller.perform_caching = true
config.cache_store = :dalli_store
routes.rb
resources :communities do
resources :topics do
resources :comments do
end
end
-------------------------------------Community----------------------------------------
controllers/communities_controller.rb#index&show
caches_page :show
caches_action :edit, :new
def index
....
if params[:sort] == 'new'
#communities = Community.scoped.page(params[:page]).order("created_at DESC")
elsif params[:sort] = 'popular'
#communities = Community.scoped.page(params[:page]).order("follow_count DESC")
elsif params[:sort] = 'reputation'
#communities = Community.scoped.page(params[:page]).order("reputation_count DESC")
else
#communities = Community.scoped.page(params[:page]).order("created_at DESC")
end
....
end
def show
#community = Community.find(params[:community_id])
#comments #community.comments.page(params[:page]
#topics = #community.topics.limit(10)
end
views/communities/index.html.erb
note: However, the content will be the same even if I move to next params[:page]:( It seems caching recognize different page as the same contents...
#here, the url will could be something like this example.com/communities?utf8=✓&location=14&genre=3&search=strawberry
But this is going to create gigantic petterns of the caches:(
So I want to make it work only when params[:sort] was not empty. Because, no other parameters come with when params[:sort] is not empty.
It could be like, example.com/communities?sort=new, example.com/communities?sort=popular, example.com/communities?sort=reputation
...
<% if params[:sort] %>
<% #key = "community_index_" + params[:sort] + params[:page] %>
<% cache(:controller => "communities", :action => "index", :action_suffix => #key) do %>
<%= page_entries_info(#communities, :entry_name => 'community').html_safe %>
<%= paginate #communities, :window => 4 %>
<% #communities.each do |community| %>
<%= render 'communities/community', :community => community %>
<% end %>
<% end %>
<% end %>
...
views/communities/show.html.erb
...
<% #key = params[:community_name] + "_community_show_information" %>
<% cache(:controller => "communities", :action => "show", :action_suffix => #key) do %>
<h2>Information</h2>
<div class="CommunityInformation">
<%= render 'communities/information'%>
</div>
<% end %>
<% #key = params[:community_name] + "_community_show_wall_" + params[:page] + %>
<% cache(:controller => "communities", :action => "show", :action_suffix => #key) do %>
<%= paginate #comments, :window => 4 %>
<h2>Topic</h2>
<div class="WallInformation">
<% #comments.eager.recent.each do |comment| %>
<%= render 'communities/comment', :comment => comment %>
<% end %>
</div>
<% end %>
<% #key = params[:community_name] + "_community_show_topics" %>
<% cache(:controller => "communities", :action => "show", :action_suffix => #key) do %>
<h2>Topic</h2>
<div class="TopicInformation">
<% #topics.eager.recent.each do |topic| %>
<%= render 'communities/topic', :topic => topic %>
<% end %>
</div>
<% end %>
...
models/community_sweeper.rb
class CommunitySweeper < ActionController::Caching::Sweeper
observe Community
def after_save(record)
expire_fragment(url_for(:action => 'index', :only_path => true) + '?????(all the caches related to the community#index)')
expire_fragment(url_for(:action => 'show', :only_path => true) + '?????(the cache related to the particular community#show)')
end
end
end
-------------------------------------Topic----------------------------------------
controllers/topics_controller.rb#index&show
caches_page :show
caches_action :edit, :new
def index
....
#community = Community.find(params[:community_id])
#topics = #community.topics.page(params[:page]
....
end
def show
#topic = Topic.find(params[:id])
#comments = #topic.comments.page(params[:page]
end
views/topics/index.html.erb
...
<% #key = params[:community_id] + "_topic_index_" + params[:page] %>
<% cache(:controller => "topics", :action => "index", :action_suffix => #key) do %>
<%= page_entries_info(#communities, :entry_name => 'community').html_safe %>
<%= paginate #communities, :window => 4 %>
<% #communities.each do |community| %>
<%= render 'communities/community', :community => community %>
<% end %>
<% end %>
<% end %>
...
views/topics/show.html.erb
...
<% #key = params[:community_name] + "_topic_show_" + params[:id] + "_comments" + params[:page] %>
<% cache(:controller => "topics", :action => "show", :action_suffix => #key) do %>
<%= paginate #comments, :window => 4 %>
<%= page_entries_info(#comments, :entry_name => 'comment').html_safe %>
<h2>Comment</h2>
<div class="CommentInformation">
<% #comments.each do |comment| %>
<%= render 'topics/comment', :comment => comment %>
<% end %>
</div>
<% end %>
...
models/topic_sweeper.rb
class TopicSweeper < ActionController::Caching::Sweeper
observe Topic
def after_save(record)
expire_fragment(url_for(:action => 'index', :only_path => true) + '?????(all the caches related to the topic#index)')
expire_fragment(url_for(:action => 'show', :only_path => true) + '?????(the cache related to the particular topic#show)')
end
end
-------------------------------------Comment----------------------------------------
models/comment_sweeper.rb
class CommentSweeper < ActionController::Caching::Sweeper
observe Comment
def after_save(record)
expire_fragment(url_for(:action => 'index', :only_path => true) + '?????(all the caches related to the topic#index)')
expire_fragment(url_for(:action => 'show', :only_path => true) + '?????(the cache related to the particular topic#show)')
end
end
I am following on on from my answer to
How should I set up dalli for a dynamic page with lots of content updates?
Please note that I am not an expert but I did try your approach and abandoned it as it just kept getting more and more complex as my application grew and I went for the cache key fragment based approach mentioned in the last question. There are a few things to bear in mind.
The cache sweeper approach requires you to make sure that the sweeper is up to date as you make changes to your application which means extra maintenance and testing work.
In a dynamic application you probably wont be able to easily cache whole pages especially if the views show information from many models.
You wont be able to deal with the paging issue unless the page parameter becomes part of your cache keys.
When your cache gets full, memcached will simply remove the least used / oldest cache items when putting in a new cache fragment.
So there is no issue with you just pushing in a new cache fragments when your models change and letting memcached clear out the old out of date cache items.
Therefore fragment caching in your views will probably be the easiest solution as it will deal with paging and you wont have to deal with manual cache invalidation
So looking at one of your views, what I might do is
<% if params[:sort] %>
<%= page_entries_info(#communities, :entry_name => 'community').html_safe %>
<%= paginate #communities, :window => 4 %>
<% #communities.each do |community| %>
<% cache(community, :suffix => "community_index") do
<%= render 'communities/community', :community => community %>
<% end %>
<% end %>
<% end %>
What I have done is cache the rendering of each community record and paging becomes irrelavent

Setting the params:id issue

I am editing the index.html.erb page in the sources view
<% #sources.each_slice(20) do |s| %>
<div class="span3">
<% s.each do |i| %>
<div><%= link_to(truncate(i.name, :length => 30) , sources_path(i.id), :class => 'action show') %></div>
<% end %>
</div>
<% end %>
The sources.controller is..
def index
#sources = Source.search(:search=>params[:search])
respond_to do |format|
format.html # index.html.erb
format.json { render json: #sources }
end
end
Q: When the params[:search] is valid. Why does the i.name info get presented and the i.id doesn't...How can I get the i.id?
It's probably not related to the id, it's most likely related to the fact that you're using sources_path instead of using source_path.

Rails: Ajax/jQuery Issue with Pagination

I have a comment model that posts under a micropost both are paginated and both show on the same page.
I have done everything from http://railscasts.com/episodes/174-pagination-with-ajax?view=asciicast and everything should work but the problem is that both micropost and commet are paginated and both are on the same page.
The links for both pagination turns into this href="/users/2?page=2" rather than href="/users/2/micropost?page=2" or href="/users/2/comment?page=2". I am unsure how to go about solving this problem. Here are some of my code. All suggestions are much appreciated!
Micropost Render HTML
<table class="microposts">
<% if microposts.any? %>
<%= render microposts %>
<%= will_paginate microposts, :page_links => false %>
<% else %>
<div class="EmptyContainer"><span class='Empty'>Add a thread!</span></div>
<% end %>
</table>
Comment Section HTML
<div id='CommentContainer-<%= micropost.id%>' class='CommentContainer Condensed2'>
<div class='Comment'>
<%= render :partial => "comments/form", :locals => { :micropost => micropost } %>
</div>
<div id='comments'>
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<%= render comments %>
<%= will_paginate comments, :class =>"pagination" %>
</div>
</div>
User Controller for the Show Page
def show
#user = User.find(params[:id])
#comment = Comment.find(params[:id])
#micropost = Micropost.new
#comment = Comment.new
#comment = #micropost.comments.build(params[:comment])
#comments = #micropost.comments.paginate(:page => params[:page], :per_page => 5)
#microposts = #user.microposts.order('created_at DESC').paginate(:per_page => 10, :page => params[:page])
respond_to do |format|
format.html
format.js
end
end
to separate the two, you should go here
http://blog.devinterface.com/2011/08/tips-multiple-pagination-with-will_paginate/
you can pass in separate params, so that only one will paginate even if both are on the same page

Resources