Destroy a post after 30 days from its creation - ruby-on-rails

For learning purposes I created a blog, now I want to destroy a post automatically after 30 days from its creation. how can I do it?
This is my Posts controller
def index
#posts = Post.all
end
def create
#post = current_user.posts.new(post_params)
#post.save
redirect_to posts_path
end
def destroy
#post.destroy
redirect_to posts_path
end

I would set up a task with whenever that runs every 1 day.
To generate a task:
rails g task posts delete_30_days_old
Then on the created file (lib/tasks/posts.rb), add the following code:
namespace :posts do
desc "TODO"
task delete_30_days_old: :environment do
Post.where(['created_at < ?', 30.days.ago]).destroy_all
end
end
This is of course if you want to delete the posts that have more than 30 days, other answers might as well work but I would rather have my database with clean data that I'll use on my application.

Posts will be stored in your database. The model is what interacts with your database. Your controller never sees the database, it only sees what the model shows it. If you wanted to pull from the database using your model inside the controller you could do it with this code.
#posts = Post.where('created_at >= :thirty_days_ago', thiryty_days_ago: Time.now - 30.days)
Post in this code calls you app/model/Post.rb which inherited active record. .where is the active record method that looks at your database based on the stuff you define. Here we have defined to pull only rows where the created_at column has a time in it that is 30 days ago.
If you look inside your database you'll notice the created_at column was automagically put in there for you.

Along with the aforementioned whenever gem, you can also use two gems called Sidekiq and Sidetiq for scheduling tasks/workers.
I've been using these on a large app at work and am very pleased with it. It's fast (uses Redis, added with a simple gem, reliable, and easy to use).
# in app/workers/clean_posts.rb
class CleanPosts
include Sidekiq::Worker
include Sidetiq::Schedulable
recurrence { monthly }
def perform
# stealing from toolz
Post.where('created_at >= :thirty_days_ago', thiryty_days_ago: Time.now - 30.days).destroy_all
end
end
This will, however, remove the posts from your DB and they will no longer be accessible by your application.

To achieve desired result, you need to change your index action like this:
def index
#posts = Post.where(created_at: 30.days.ago..Time.now)
end
In such way, you won't need to destroy posts and you will get the desired result.
If you need to limit access to the older posts, then you can use:
def show
#post = Post.where(created_at: 30.days.ago..Time.now).find(params[:id])
end
And, if we are speaking about code beauty, then you should move where(created_at: 30.days.ago..Time.now) part to a scope in your model, like this:
class Post
...
scope :recent, -> { where(created_at: 30.days.ago..Time.now) }
end
And use it like this:
Post.recent #=> to get list of recent posts
Post.recent.find(params[:id]) #=> to get recent post with specified id

You can not do that from your controller, you need to add some functionality to your application.
You will need a cron job running everyday that will look for posts that are more than 30 days old and destroy them.
eg Post.where('created_at < ?', 30.days.ago)
For handling the cron jobs better you might consider using the whenever gem that helps a lot and keeps the cron setup in your app.

Related

Rails partial need to be rendered twice with different content, but output is equal

I have to output 2 blocks on the same page, each one of them must contain 3 random posts from database.
I have simple model
class Post < ActiveRecord::Base
scope :random, -> { order('RANDOM()') }
end
I put the ActiveRecord code in application_helper.rb:
module ApplicationHelper
def random_posts(num = 3)
posts = Post.uncached do
Post.random.limit(num)
end
end
end
then in layout I use this call twice (Slim template engine used)
= render random_posts
which uses this partial app/views/posts/_post.html.slim
a.btn = post.title
Blocks are filled with 3 random posts from database, but they are the same in each blocks! Why is it so? Each block has to contain different posts.
I've created a repo here with simple demonstration
I got this to work by flipping uncached to cache. I was experimenting on the Post model, but you could probably drop this in your helper just as easily:
class Post < ActiveRecord::Base
def self.random_posts(n = 3)
cache do
random.limit(n)
end
end
end
For two calls of the method using uncached, the ActiveRecord log lines are Post Load ... and CACHE ..., but using cache, they are both Post Load.... I really wish I could explain why this works, but it's completely counterintuitive and makes no sense to me.
Forking your code, it seems that the collection Post.random is being cached in Rails in some way. If you add a debugger on the random_posts in ApplicationHelper:
Post.random.map(&:id)
Will have the same collection every time.
Taken from this blogpost, you could use this as an alternative:
In ApplicationHelper.rb:
def self.random_posts(num = 3)
ids = Post.pluck(:id).shuffle[0..4]
Post.where(id: ids)
end

Create Rails model with argument of associated model?

I have two models, User and PushupReminder, and a method create_a_reminder in my PushupReminder controller (is that the best place to put it?) that I want to have create a new instance of a PushupReminder for a given user when I pass it a user ID. I have the association via the user_id column working correctly in my PushupReminder table and I've tested that I can both create reminders & send the reminder email correctly via the Rails console.
Here is a snippet of the model code:
class User < ActiveRecord::Base
has_many :pushup_reminders
end
class PushupReminder < ActiveRecord::Base
belongs_to :user
end
And the create_a_reminder method:
def create_a_reminder(user)
#user = User.find(user)
#reminder = PushupReminder.create(:user_id => #user.id, :completed => false, :num_pushups => #user.pushups_per_reminder, :when_sent => Time.now)
PushupReminderMailer.reminder_email(#user).deliver
end
I'm at a loss for how to run that create_a_reminder method in my code for a given user (eventually will be in a cron job for all my users). If someone could help me get my thinking on the right track, I'd really appreciate it.
Thanks!
Edit: I've posted a sample Rails app here demonstrating the stuff I'm talking about in my answer. I've also posted a new commit, complete with comments that demonstrates how to handle pushup reminders when they're also available in a non-nested fashion.
Paul's on the right track, for sure. You'll want this create functionality in two places, the second being important if you want to run this as a cron job.
In your PushupRemindersController, as a nested resource for a User; for the sake of creating pushup reminders via the web.
In a rake task, which will be run as a cron job.
Most of the code you need is already provided for you by Rails, and most of it you've already got set in your ActiveRecord associations. For #1, in routes.rb, setup nested routes...
# Creates routes like...
# /users/<user_id>/pushup_reminders
# /users/<user_id>/pushup_reminders/new
# /users/<user_id>/pushup_reminders/<id>
resources :users do
resources :pushup_reminders
end
And your PushupRemindersController should look something like...
class PushupRemindersController < ApplicationController
before_filter :get_user
# Most of this you'll already have.
def index
#pushup_reminders = #user.pushup_reminders
respond_with #pushup_reminders
end
# This is the important one.
def create
attrs = {
:completed => false,
:num_pushups => #user.pushups_per_reminder,
:when_sent => Time.now
}
#pushup_reminder = #user.pushup_reminders.create(attrs)
respond_with #pushup_reminder
end
# This will handle getting the user from the params, thanks to the `before_filter`.
def get_user
#user = User.find(params[:user_id])
end
end
Of course, you'll have a new action that will present a web form to a user, etc. etc.
For the second use case, the cron task, set it up as a Rake task in your lib/tasks directory of your project. This gives you free reign to setup an action that gets hit whenever you need, via a cron task. You'll have full access to all your Rails models and so forth, just like a controller action. The real trick is this: if you've got crazy custom logic for setting up reminders, move it to an action in the PushupReminder model. That way you can fire off a creation method from a rake task, and one from the controller, and you don't have to repeat writing any of your creation logic. Remember, don't repeat yourself (DRY)!
One gem I've found quite useful in setting up cron tasks is the whenever gem. Write your site-specific cron jobs in Ruby, and get the exact output of what you'd need to paste into a cron tab (and if you're deploying via Capistrano, total hands-off management of cron jobs)!
Try setting your attr_accessible to :user instead of :user_id.
attr_accessible :user
An even better way to do this however would be to do
#user.pushup_reminders.create
That way the user_id is automatically assigned.
Use nested routes like this:
:resources :users do
:resources :pushup_reminders
end
This will give you params[:user_id] & params[:id] so you can find your objects in the db.
If you know your user via sessions, you won't need to nest your routes and can use that to save things instead.
Using restful routes, I would recommend using the create action in the pushup_reminders controller. This would be the most conventional and Restful way to do this kind of object creation.
def create
#user = User.find(params[:user_id]
#reminder = #user.pushup_reminders.create()
end
If you need to check whether object creation was successful, try using .new and .save

Show Last modified tables/records Rails 3.2

I still can't figure out how to implement this as i am a newbie with this. some people helpt me and said i had to use audited, so it did. this is my controller:
def show
add_breadcrumb 'Contract Bekijken', :contracten_path
#contracten = Contracten.find(params[:id])
#audits = #contracten.audits.collect { |a| a.created_at }
respond_to do |format|
format.html # show.html.erb
format.json { render json: #contracten }
end
end
Here's a pastie of my whole controller. http://pastie.org/4270702
But i don't know if this is right or how to implement this to my views.
I hope someone really can help because i really need this to work this week.
Thanks.
i have a rails app where i can store contracts in a database, it also has persons and factories tables in the database.
Now i would like to have a last modified table.
I would like when people update/add a new record to the database, that it will show the modifications in the div right on the screenshot.
Thanks :D
What you need is a audit history of edits.
You can either implement that yourself (may make sense if there is custom business logic involved you really want to write yourself) by hooking into the ActiveRecord callbacks for that model.
A sample implementation may look like this:
class Contract < ActiveRecord::Base
after_save :audit
def audit
log = AuditLog.new(:user_id => current_user, :action => :update, ...)
log.save
end
end
This would assume you have a AuditLog Model that contains the appropriate fields you want to log to and in your audit method you write to that.
Or, a simpler way is to use the Audited gem that does that for you (but may come with some limitations).
From the documentation of Audited it seems like you can simply fetch a Contract record and use the audits on that model to then access the audited information.
Sample
def show
#contract = Contract.find(params[:id])
#audits = #contract.audits.collect { |a| a.created_at }
end
Now you have all the timestamps of the audits in the #audits variable and can access them from the view using <% #audits.each do ....
From your question it seems like you just need a list based on the updated_at field.
How about - #contract_list = Contract.all.order( "updated_at DESC").limit(10)
Then you can iterate over the list in the view.
Nice looking page!

Creating logs & history in Rails3

I'm trying to create some logs and history about users in my app.
For example, I'd like to know how many posts any user had total, on any given day. And some other cool metrics.
Off the bat, my assumption is that the only way to do this, is to have a cron job running some tasks, that calculates these numbers, and stores them for each user, every day, in a new table.
Is there a better or alternative way to go about this?
You could use a plug-in such as make_resourceful. In the case of posts, you could override the after :create method to post to a table that tracks the number of posts for the user. In the case of page views, you could override the after :show method.
You must include the the make_resourceful methd in the controller class. For example:
class BlogPostsController < ApplicationController
make_resourceful do
actions :all
response_for after :create do |format|
BlogPostAudit.create (:user => current_user)
end
end
end

Ruby on Rails: How to set up "find" options in order to not use cache

In Ruby on Rails you can find records from the database with this syntax:
<model_name>.find_by_<field_name>()
Examples: User.find_by_email('test#test.test'), User.find_by_id(1), ...
Time ago, if I am not wrong, I read somewhere that you can explicitly disable caching for 'find' operations, but I can not remember how.
Can someone help me remember?
You can use ActiveRecord::QueryCache.uncached like this:
User.find_by_email('test#test.test')
User.find_by_email('test#test.test') # Will return cached result
User.uncached do
User.find_by_email('test#test.test')
User.find_by_email('test#test.test') # Will query the database again
end
In a controller, it would look something like this:
def show # users#index action
User.uncached do
#user = User.find_by_email('test#test.test')
#another_user = User.find_by_email('test#test.test') # Will query database
end
User.find_by_email('test#test.test') # Will *not* query database, as we're outside of the Users.uncached block
end
Obviously, in a model, you just have to do:
class User < ActiveRecord::Base
def self.do_something
uncached do
self.find_by_email('test#test.test')
self.find_by_email('test#test.test') # Will query database
end
end
end
User.do_something # Will run both queries
(Note: assuming Rails3, since Rails2 doesn't have default caching.)
This should work as you want it to out of the box:
Queries caches are destroyed after each action ( http://guides.rubyonrails.org/caching_with_rails.html paragraph 1.5)
In addition, it seems (http://ryandaigle.com/articles/2007/2/7/what-s-new-in-edge-rails-activerecord-explicit-caching) that caches are also destroyed on attribute/record updates
Do you have a specific use case not covered by the default configuration?

Resources