Update user's last sign in time when logging in again - ruby

for example:
if the user login at 10:45 AM and logs out and then when he login's again the value of last_sign_in should be 10:45 AM and current_sign_in value should be current Time.
def method_name
old_current, new_current = self.current_sign_in_at, Time.now.utc
self.last_sign_in_at = old_current || new_current
self.current_sign_in_at = new_current
end

That method looks fine, though it's not currently saving the attributes.
Assuming this method is in user.rb, you just need to add:
def method_name
old_current, new_current = current_sign_in_at, Time.now.utc
self.last_sign_in_at = old_current || new_current
self.current_sign_in_at = new_current
save
end
You could also look at using update_attributes or similar.
It sounds like you'll just need to call this method on the user in the relevant controller action. If you post a little more code, I can tailor this, though hopefully that'll get you in the right direction.

Related

How do I tack a string onto a variable and evaluated the entire thing as a variable in Ruby?

I have the following Ruby code:
module BigTime
FOO1_MONEY_PIT = 500
FOO2_MONEY_PIT = 501
class LoseMoney
##SiteName = 'FOO1'
#site_num = ##SiteName_MONEY_PIT
def other_unimportant_stuff
whatever
end
end
end
So, what I'm trying to do here is set the SiteName and then use SiteName and combine it with the string _MONEY_PIT so I can access FOO1_MONEY_PIT and store its contents (500 in this case) in #site_num. Of course, the above code doesn't work, but there must be a way I can do this?
Thanks!!
If you want to dynamically get the value of a constant, you can use Module#const_get:
module BigTime
FOO1_MONEY_PIT = 500
FOO2_MONEY_PIT = 501
class LoseMoney
##SiteName = 'FOO1'
#site_num = BigTime.const_get(:"#{##SiteName}_MONEY_PIT")
end
end
Do not, under any circumstance, use Kernel#eval for this. Kernel#eval is extremely dangerous in any context where there is even the slightest possibility that an attacker may be able to control parts of the argument.
For example, if a user can choose the name of the site, and they name their site require 'fileutils'; FileUtils.rm_rf('/'), then Ruby will happily evaluate that code, just like you told it to!
Kernel#eval is very dangerous and you should not get into the habit of just throwing an eval at a problem. It is a very specialized tool that should only be employed when there is no other option (spoiler alert: there almost always is another option), and only after a thorough security review.
Please note that dynamically constructing variable names is already a code smell by itself, regardless of whether you use eval or not. It pretty much always points to a design flaw somewhere. In general, you can almost guaranteed replace the multiple variables with a data structure. E.g. in this case something like this:
module BigTime
MONEY_PITS = {
'FOO1' => 500,
'FOO2' => 501,
}.freeze
class LoseMoney
##SiteName = 'FOO1'
#site_num = MONEY_PITS[##SiteName]
end
end
You can refactor this as to use a Hash for your name lookups, and a getter method to retrieve it for easy testing/validation. For example:
module BigTime
MONEY_PITS = { FOO1: 500, FOO2: 501 }
MONEY_PIT_SUFFIX = '_MONEY_PIT'
class LoseMoney
##site = :FOO1
def initialize
site_name
end
def site_name
#site_name ||= '%d%s' % [MONEY_PITS[##site], MONEY_PIT_SUFFIX]
end
end
end
BigTime::LoseMoney.new.site_name
#=> "500_MONEY_PIT"

Checking if a record exists in Sinatra/DataMapper

I currently generate a user's profile page using their serial ID, like so:
get '/users/:id' do
#user = User.get(params[:id])
end
This works great, until a number is entered that doesn't exist in the database.
I'm aware I can change User.get to User.get! to return an ObjectNotFoundError error if the record isn't found, but I'm not sure how I can use this to my aid.
I used to use .exists? when using RoR.
Any suggestions?
Thanks in advance!
Edit: I'm going to leave the question unanswered, as I haven't actually found a solution to what I asked in the title; however, I did manage to solve my own problem by checking to see if the :id entered is higher than the amount of users that exist in the database, like so:
if params[:id].to_i > User.count
"This user does not exist."
else
#users_id = User.get(params[:id])
erb(:'users/id')
end
You already have the correct code:
#user = User.get(params[:id])
If no user exists with the given id, then #get will return nil. Then you can just do a conditional:
#user = User.get params[:id]
if #user
# user exists
else
# no user exists
end
This is a very common pattern in Ruby which takes advantage of the "truthiness" of anything other than false or nil. i.e. you can say if 0 or if [] and the condition will evaluate to true
You can recreate a .exists? method:
class User
def self.exists?(id_or_conditions)
if id_or_conditions.is_a? Integer
!! User.get id_or_conditions
else
!! User.first id_or_conditions
end
end
end
#get is similar to #find in rails, except it doesn't raise an error if the record is not found. #first is similar to #find_by in rails.

In Rails, why are my controller params being modified by the class

I have a controller action somewhat similar to this:
def reports
puts params
#stats = Client.stats(params)
puts params
end
The initial params might look like this:
{ end: '2012-01-01 21:00:19' }
And in my Client model, I have this:
def self.stats(opts)
opts[:start] = (Time.now - 30.days).to_i
...do some calculations..
return stats
end
If I inspect the params object that was sent before and after the function runs, I can see it's been modified by the self.stats method.
In the example above, I'm not sending 'start' in the initial params, the method adds it for the calculations - as expected.
What I was not expecting was that the function would modify the original hash!
Can someone explain why this is happening?
--EDIT--
I forgot to say I tried to create a copy of the params and use that instead, same issue.
def reports
a = params
#stats = Client.stats(a)
puts params
end
The params are still updated?!
That's, because your function call gets a reference to the params not a copy. If you do something like opts[:start] = (Time.now - 30.days).to_i you are editing the params object.
a = params: now both variables point to the same place in the memory. You copied the pointer only.
Google for ruby object copy or ruby deep copy or search at stackoverflow for it. At a first try you could try params.clone.
Whenever you are updating any value of params, take a copy of params like this
a = params.clone
It will create a new element in memory
if you do like this it wont create a new element in memory it will point the same memory
a = params
Try this

Assign variable as either output of equation OR nil if equation can't run (circumventing NoMethodError)

I'm saving an object as such:
#rentalrequest = RentalRequest.new do |rr|
rr.delivery_start = Time.zone.parse(request_params[:deliverydate] + " " + request_params[:deliverytime_start]).utc
...
end
Every once in a while, my front end validation fails, and somehow, the form is posted even though deliverydate and deliverytime_start are blank. In this case, the controller breaks with a NoMethodError because this statement doesn't make sense:
Time.zone.parse("")
However, rather than having to write a rescue for when this happens, I feel like it's so much easier if I can just say rr.delivery_start = nil if Time.zone.parse doesn't work. That way, the back end validation on the #rentalrequest object kicks in and serves as a rescue.
But I'm not sure how to write the rr.delivery_start = nil if Time.zone.parse doesn't work (like... if any part of it doesn't work)
Thoughts?
How about checking if those params exist instead?
#rentalrequest = RentalRequest.new do |rr|
rr.delivery_start = nil
if request_params[:deliverydate].present? && request_params[:deliverytime_start].present?
rr.delivery_start = Time.zone.parse(request_params[:deliverydate] + " " + request_params[:deliverytime_start]).utc
end
...
end
Your calculation is in the wrong place, your model should be maintaining its own state by itself. Something like this perhaps:
class RentalRequest < ActiveRecord::Base
before_validation :set_delivery_start
private
def set_delivery_start
# Presumably validations will catch these conditions...
return if(!deliverydate || !deliverytime_start)
self.delivery_start = ... # whatever calculation matches the deliverydate and delivertime_start types goes here
end
end
and then you'd have validations to ensure that all three delivery values made sense.
You can require those parameters if new block is in controller.
def request_params
params
.require(:rental_request)
.permit(..., :deliverydate, :deliverytime_start)
.require(:deliverydate, :deliverytime_start)
end

Why won't Sequel write my database table?

controller/makenew.rb
class MakeController < Controller
map '/makenew'
#require 'model/debate'
def debate
if request.post? #this line is potentially dangerous!
#---> 1/3 fetch postdata
data = request.subset(:question, :type, :category, :assertion)
data['user_id'] = user.id #id = request.params['id']
#---> 2/3 check permissions
if user.points < 40
flash[:error] = 'You don\'t have enough points to make a debate.'
redirect_referrer
else
debate = Debate.new
end
#---> 3/3 modify database
begin
debate.save(data)
flash[:success] = success
flash[:form_data] = debate
redirect 'debates'
rescue => e
Ramaze::Log.error(e)
#flash[:form_errors] = debate.errors
#flash[:error] = data
flash[:error] = e
#flash[:error] = 'Failure whilst saving. Contact technical support!'
redirect 'debates' #redirect_referrer
end
#|
end #closes posting conditional
end #closes makesave
end
The error I get is.
SQLite3::ConstraintException: debates.question may not be NULL
I have checked the postdata for data.question and it is not null.
What is going on?
You need to pass 'data' to #update. Thus:
debate.save(data)
is wrong, you have to do:
debate.update(data)
debate.save
If you don't do this, your debate object has no member assigned and thus its question member is nil, violating your DB constraints.
See the differences between #save and #update here:
Update : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-update
Save : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-save
In a nutshell: #save will save the current model instance to the database, while #update will change a bunch of instance attributes in one operation.
But you have to remember that changing a model instance's attributes DOES NOT write them to the database. You always have to call #save explicitly.
Are you sure that your model accepts mass assignment of primary keys?
Try calling Debate.unrestrict_primary_key
You can check the rules in the Sequel documentation.

Resources