How to use the new ActiveSupport::Testing::TimeHelpers inside controller - ruby-on-rails

Edit to make it more clear
Like the title tells how to use the awesome ActiveSupport::Testing::TimeHelpers#travel_to method inside my controller. Like in tests, I want to achieve something like this:
SomethingConroller < ApplicationController
def index
travel_to some_date do
# some stuff that it depends on the current_date
end
end
end
Tried include the module:
include ActiveSupport::Testing::TimeHelpers
but I got:
uninitialized constant ActiveSupport::Testing
Hoping that the date traveled to will be applied to the view, view_helpers, controller_action

If you must, then add require 'active_support/testing/time_helpers' at the top of the file.
Although I've no idea what you're doing with that code. Try this instead:
SomethingConroller < ApplicationController
def index
#instance = SomeModel.find_by_date(12.days.ago)
end
end

I think you're messing up your concepts here. You should use time travel to put tests in a certain time so that you can test a scenario at that new time. For example, lets say users have to renew their subscription after one year.
create the user(now).
travel to the time a year from now
Make sure that when the user logs in at travelled to time they are duly notified.
To manipulate date for queries, use 12.days.ago, 12.days.from_now, this can be used with seconds,days, minutes, years

Related

How to track if user make any actions Rails app

I'm creating app with lessons/tests after it and right now I need to create a report card with some information. I want to add column to Users which will track time spent online(without idle time). For example student is pressing any buttons on site(just for example), it means that student is online and the (current_user.online_time should not stop), but if student did nothing more than 5 minutes - (current_user.online_time should stop). I found a gem devise lastseenable, but can't imagine right know how to make it work according to my wishes. Gem tracks when U did any actions only with User(create/update/delete/ or with models which belongs to user), without tracking any other moves. Can someone give me any ideas?
It is certainly possible to put together a simple user tracking feature without using an external gem specifically built for this purpose. Here is a list of the required implementation steps:
1. Add a total_time_online and a last_seen_at field to User
total_time_online will contain the number of seconds the user was seen online
last_seen_at will hold the date and time the user last interacted with the site
2. Add an active_now! method to User
This method will be called whenever the user is interacting with the site. It is responsible for incrementing the total_time_online value and updating the last_seen_at field:
class User
ActivityThreshold = 5.minutes
# ...
def active_now!
time_since_last_activity = [Time.now - last_seen_at, 0].max
if time_since_last_activity <= ActivityThreshold
self.total_time_online ||= 0
self.total_time_online += time_since_last_activity
end
self.last_seen_at = Time.now
save!
end
# ...
end
This will only increment the total_time_online if the last interaction was less than 5 minutes ago.
3. Call active_now! on the current user on every request
A global before_action should do the trick:
class ApplicationController < ActionController::Base
# ...
before_action :record_user_activity
# ...
private
# ...
def record_user_activity
current_user.active_now! if current_user
end
end

How to prevent actions at specific date and time?

I would like to allow the users to 'create' and 'update' their submissions (bets) until a specific date and time at which point their bets are final (can no longer be created or updated). This process would repeat each week.
I'm fairly new to Rails and I'm not sure if there is a term for this or what to search for.
Can anyone point me in the right direction?
Probably the easiest way to achieve this is just to add a before_filter (Rails 3.x) or before_action (Rails 4.x) to your controller. You can do so like this:
Assume you have submissions_controller.rb with create/update actions like so - add a before filter that will only apply to the create and update actions. You can then implement a private method in the controller to redirect the user back to your root_path or elsewhere and give a flash message as to why.
class PagesController < ApplicationController
before_filter :check_if_bets_are_final, :only => [:create, :update]
def create
...
end
def update
...
end
private
def check_if_bets_are_final
if Time.now >= Time.new(2014, 02, 20)
flash[:error] = "You can no longer modify or submit new bets!"
redirect_to root_path
end
end
end
Aside from your controller action though, it will probably be safer to implement a model-level validation/check to reject it if the date is past, just to be safe (or if you have other ways to update that object in the future). You can do this through the model hook before_save, in which you can pretty much do a similar check that I have given above.
Also, the other caveat is that comparing Time.now could be in a different timezone depending on where your server is. Just be cognisant of this when you do your checks, and cast the time properly with this in mind.
Since you didn't provide a specific implementation, I'm not quite sure if you're having trouble specifically with Ruby or Rails. However, given your question, I would store a datetime variable in your database when the user creates the bet. Every time the user tries to 'update' the bet, check in the database whether or not it's been past that specific time away from the bet creation. Hope this helps.

Destroy a post after 30 days from its creation

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.

Create a logic captcha from scratch

I haven't touched a scrap of code yet, but here's my thoughts on how to do this:
Create a :interactions entry in my session hash. This will contain an array of time stamps. Every time a user goes through any action, the time they did this will be appended to the :interactions entry. The array will be initialized in my sessions controller, and timestamps appended to it via a filter in my application controller:
class SessionsController < ApplicationController
def create
create_session
session[:interactions] = []
end
end
class ApplicationController < ActionController::Base
after_action :log_time
private
def log_time
session[:interactions] << Time.now.to_i
end
end
Then, create another action in my application controller, the one tasked with launching the recaptcha if the user's behaviour is suspicious. All it does is see when we have 20 entries in our session[:interactions] array, find out the time elapsed between each pair of consecutive entries, and then find the average time elapsed between these interactions. If the average time is under two minutes, the recaptcha is launched. The session[interactions] array is then reset.
class ApplicationController < ActionController::Base
after_action :log_time
before_action :launch_captcha
private
def launch_captcha
if session[:interactions].length == 20
elapsed = []
session[:interactions].each_slice(2) do |a, b|
elapsed << b - a
end
total = elapsed.inject(:+)
average = total / 20
if total < 120
# this a part I'm really not sure how to do:
# various instance variables should be populated here
redirect_to 'application/launch_captcha.html.erb'
end
session[:interactions] = []
end
end
def log_time
session[:interactions] << Time.now
end
end
Now, the fact the session[:interactions] is reset may be a bit of a weakness; all bets are off for those twenty interactions. But I want to build on the above logic, maybe add session[:captchas_sent], to the session hash (or even have captchas_sent as a column and save it to the user's record), and if the session[:captchas_sent] is x amount or y amount, warnings or temporary bans could come into effect.
What are your thoughts on the above way of monitoring user behaviour?
Here's where my knowledge of rails is starting to break down though. Once I've redirected the user to the recaptcha page, how should I proceed? I have two tables, questions and answers with a has_many belongs_to relationship between them respectively.
So a random question will come from my questions table, and then I'll have a form that pertains to an answer. It will be an ajax form, and have just one field, a text field for the answer. The action the form links to, human test, will see if the answer given is equal to one of the question's answers. But how should the question's id be passed into this action? It couldn't be a simple hidden field, because once the spammer knows the answer to one question, his script could always set the id to that one question. So the params hash or the sessions hash maybe? I need some advice here guys.
I also don't really know how the human test method should proceed once the it finds the user's answer is equal to one of the question's answers:
Let's say the user is submitting a comment. They fill in the comment, hit the submit button, the launch_captcha action kicks in, and redirects them to 'application/launch_captcha.html.erb'. What has happened to the data in the comment create form? Once they've answered the captcha correctly, how should the human_test method proceed? How could it go on to submit their comment as usual? I just don't know how to do that...I need to create an action that is called before the create action of a form...and..argh I just don't know. Help guys!

Where to put this kind of calculation in Rails?

I have started to learn ROR and come along with creating one simple application.
Basicly the app uses calculation for the difference in months between today and another date stored in the database. I have no idea where is the most convenient way to put this kind of code.
Currently, is placed in the view where i subtract today from the date in the database :
#user.html.erb
Month difference : <%= (Date.today - #user.date_signed).to_i/30
and controller :
class UserController < ApplicationController
def user
#user = User.find(params[:id])
Is there any better way i can accomplish the same ? And if so how ?
Thank you
It belongs in the model, since you want to handle it as another attribute of user.
class User < ActiveRecord::Base
def months_since_signed
(Date.today - self.date_signed).to_i/30
end
end
Then, in your view, just call the method with the instance of User as the receiver.
Month difference : <%= #user.months_since_signed %>

Resources