I have a test application that has a subscription. The subscription expires and I have a controller that handles the renew subscription(I know it's not good practice. I hired a cheap freelancer to do it and I don't really know how to program). The only way I know how to trigger a controller is with a link. I have a variable called 'days_til_expire' that will count down to zero.
Main Question: Is there a way to automatically trigger a refresh that will activate the controller I need. I'm thinking:
if days_till_expire == 0
page refresh to controller#renew
end
As an alternative to Wayne's answer:
Whenever user performs a "restricted" action (click to watch a video or whatever it is that your app hides behind the paywall), check subscription status and redirect to renew page if it's expired.
No need to bother with page refreshes. Unless, of course, your app has rich client-side UI (meaning, lots of javascript).
You don't need to trigger the link, you can use polling to check if expired or not, if expired, use js to redirect user.
To further #Sergio Tulentsev's answer, which is further to #Wayne Chu's answer (answer-ception?????), this is something called authorization, and can be handled entirely in the backend.
Simple implementation:
#app/models/user.rb
class User < ActiveRecord::Base
def expired?
# logic for whether subscription has expired
end
end
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :authorized?
private
def authorized
if current_user.expired?
redirect_to root_path, notice: "Your Subscription has Expired"
end
end
end
This will fire every single time your user browses to a controller which inherits from the ApplicationController (BlogsController < ApplicationController).
--
A more integrated approach (instead of having to call a method every single time you browse to a controller) would be to validate against specific objects (using Pundit):
class PostPolicy
attr_reader :user, :post
def initialize(user, post)
#user = user
#post = post
end
def update?
user.subscribed?
end
end
The above would be accompanied by a daily process which would cycle through your users, and see if their subscription is valid. If not, it should set a value in the users db, which will then allow you to validate off it.
I've not used Pundit a lot, so the above code would need tweaking a lot. The most basic type of authorization is with roles. The alternative is object-orientated authorization, which will check against the object rather than the user.
If you need "professional help", and if you have a big budget, go to http://anadea.info (I have absolutely no affiliation with them); I worked with them in 2012 and is the reason I've been working with Rails ever since; top quality company.
Don't do it if you don't want to spend $5k+ though, it's not worth it.
Related
I'm trying to implement an e-commerce app. In it, I allow the user to browse for products and put them into the cart even before signing in. I only prompt the user to sign in upon checkout.
However, I'm losing track of the user because the user's session id changes upon sign in. Due to this, Im unable to associate the items that the user placed into the cart (stored in redis) to the user who placed them in after the user signs in to the application.
Does anyone have any idea how could this be circumvented?
Thanks.
Cheers!
Found the solution. All that needs to be done is set session.options[:renew] = false and the session id will still be the same before & after signing in.
Please refer to the implementation below
class SessionsController < Devise::SessionsController
respond_to :json
def create
super
session.options[:renew] = false
end
def destroy
logger.info "Logging out: #{current_user.email}; Session Id: #{session.id}"
$redis.del "cart_#{session.id}"
super
end
end
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.
If a user requests 2 password reset links in a row, then only the most recent one is valid.
If a user clicks on the older one, they are not immediately informed of the token being invalid. They are asked to type in the new password twice. Only upon hitting submit are they informed that the token is not valid.
Is there a standard way to change this behavior, so the user is immediately informed that they used the wrong link?
I was a bit surprised to find out that this is standard behavior - thanks for bringing it up! I would probably use a custom controller that inherits from Devise::PasswordsController and simply override :edit. The trouble is we're working with an abstract user (since no user is actually authenticated at this point), so we can't check it against a particular token in the database, and we have to check whether the given token exists.
class PasswordsController < Devise::PasswordsController
before_filter :validate_reset_password_token, only: :edit
private
def validate_reset_password_token
recoverable = resource_class.find_by_reset_password_token(params[:reset_password_token])
redirect_to root_path unless (recoverable && recoverable.reset_password_period_valid?)
end
end
This will redirect unless a user exists with the token passed. You can handle the redirection/error display however you wish.
Finally, in your routes file, change the controller used by devise for passwords:
devise_for :users, :controllers => { passwords: "passwords" }
Caveats:
I have not tested this (but I might look into it tomorrow).
There may be some additional security issues to consider - could someone realistically gain access to a random account? (probably not; Devise uses a similar method of locating a model instance using the reset token in reset_password_by_token)
By the way, there is no way to tell them that they used the wrong link and they should click the other one, as there is no way to positively identify them in the first place, and therefore no way to know that they have already generated another valid token.
There is another safety solution for this question;
class User::PasswordsController < Devise::PasswordsController
def update
super do |resource|
# Jump to super code
next unless resource.errors.any?
token_errors = resource.errors.details[:reset_password_token]
expired_error = token_errors.select { |detail| detail[:error] == :expired }
# Jump to super code
next unless expired_error.present?
message = resource.errors.full_messages_for(:reset_password_token).join(',')
return redirect_to new_user_password_path, alert: message
end
end
end
Situation: rails 3.2 app with a demo period, after which users must start paying for the service.
Question: If a user does not add a payment method, or does not choose a payment plan, what is the recommended way of restricting user access to the 'paid' part of the web app?
I need something that sorts users as follows:
if user.admin? || user.in_demo || user.has_all_payment_data
# carry on
elsif user.should_add_payment_method
# send them to add payment method page
elsif user.should_choose_plan
# send them to add plan
else
# redirect to home page or whatever
end
I've started off with a before_filter on the application controller that checks the payment status of the user on every request and redirects them accordingly (skipping this in places like the homepage/profile editing etc.), but I'm thinking there must be a better way, as it's rapidly getting too complicated and it just feels wrong having all that complexity in the application controller. I've been looking at user roles libraries like cancan but I can't find anything that fits.
There is a post by Jonas Nicklas (creator of Capybara and CarrierWave) in which he explains in some detail how to take a simpler approach than CanCan's. His approach is based on an additional plain Ruby class for each model you want to create authorization rules for.
Simple authorization in Ruby on Rails apps (Elabs blog)
They have offloaded that solution into a gem named Pundit, but it really seems simple enough to be able to implement from scratch.
Pundit gem (GitHub)
I would suggest a before_filter in the application controller, then using skip_filter in individual controllers to bypass it for actions that non-paid users can access, e.g:
class ApplicationController < ActionController::Base
before_filter :check_payment
...
end
class UserController < ApplicationController
skip_filter :check_payment, :only => [:login, :logout, ...]
...
end
This keeps the access contained to the relevant controllers, rather than needing an increasingly large :except => ... on the filter itself.
I'm shifting code from an application built in a non-standard custom PHP framework into Ruby on Rails (version 3). In the PHP version all the controllers are really fat, with thin models, which I've always disagreed with, so I'm enjoying the way Rails does validation at the model level, which is probably 90% of what's happening in these fat controllers currently.
One problem I'm facing, and unsure how to resolve however, is that of differing validation rules based on who's making the change to the model. For example, an administrator, or the original creator of the record should be able to do things like flag a record as deleted (soft delete) whereas everybody else should not.
class Something < ActiveRecord::Base
...
validates :deleted, :owned_by_active_user => true
...
end
class OwnedByActiveUserValidator < ActiveModel::EachValidator
validate_each(record, attr_name, attr_value)
# Bad idea to have the model know about things such as sessions?
unless active_user.admin? || active_user.own?(record)
record.errors.add :base, "You do not have permission to delete this record"
end
end
end
Since the model itself is (in theory) unaware of the user who is making the change, what's the "rails way" to do this sort of thing? Should I set the active user as a virtual attribute on the record (not actually saved to DB), or should I just perform these checks in the controller? I have to admit, it does feel strange to have the model checking permissions on the active user, and it adds complexity when it comes to testing the model.
One reason I'm keen to keep as much of this as possible in the model, is because I want to provide both an API (accessed over OAuth) and a web site, without duplicating too much code, such as these types of permissions checks.
It is really the controller's job to handle authorization, or to delegate authorization to an authorization layer. The models should not know about, nor have to care about, who is currently logged in and what his/her permissions are - that's the job of the controller, or whatever auth helper layer the controller delegates that to.
You should make :deleted in-attr_accessible to mass assignment via new, create, or update_attributes. The controller should check the authenticated user's authorizations separately and call deleted= separately, if the authenticated user is authorized.
There are several authorization libraries and frameworks to help with authorization or to function as an authorization layer, such as cancan.
I would solve this with a before_filter in my controller, instead of with validations in my model.
class SomethingController < ApplicationController
before_filter :require_delete_permission, :only => [:destroy]
def destroy
# delete the record
end
private
def require_delete_permission
unless current_user.is_admin || record.owner == current_user
flash[:error] = 'You do not have delete permissions'
redirect_to somewhere
end
end
end
I have come across the same issue in Rails 2.3 and finally come up with this solution. In your model you define some atribute, depending on which you switch on/off validation. Than you your control you set this attribute depending on the date available to controller (such as user privileges in your case) as follows:
Class Model < ActiveRecord::Base
attr_accessor :perform_validation_of_field1 #This is an attribute which controller will use to turn on/off some validation logic depending on the current user
validates_presence_of :field1, :if => :perform_validation_of_field1
#This validation (or any similar one) will occur only if controller sets model.perform_validation_of_field1 to true.
end
Class MyController < ActionController::Base
def update
#item = Model.find(params[:id])
#item.update_attribute(params[:item])
#The controller decides whether to turn on optional validations depending on current user privileges (without the knowledge of internal implementation of this validation logic)
#item.perform_validation_of_field1 = true unless active_user.admin?
if #item.save
flash[:success] = 'The record has been saved'
redirect_to ...
else
flash.now[:error] = 'The record has not passed validation checks'
render :action => :edit
end
end
I think that in Rails 3 it can be done in similar manner.