Rails Game Loop in Application Controller? - ruby-on-rails

Since i'm writing a game in RoR, i need to have a game loop that is responsible for checking different things every time a page refresh happens. My question is, what is the best way to implement ?
I currently include the game_loop in my application controller. Is this the best practice ?

Executing the game look as a before_filter in your ApplicationController sounds reasonable, although you may not wish to put your logic in this class:
class ApplicationController < ActionController::Base
before_filter :do_game_loop
private
def do_game_loop
Game.do_game_loop # the implementation of Game is in another file, maybe in lib
end
end
Do note that this will execute the game loop before every action in your application that involves a controller that extends from ApplicationController, including user login, logout, etc. It may be better to add the before_filter only in the controllers that definitely need to process the game loop.

Related

Passing rails requests through a series of controllers?

The question is generalized but I want to ask about a specific case which I want to solve.
I'm working with a really really smelly code base of e-commerce app and I want to refactor it. I thought I should start with the User authentication.
Problem
Before every action in any controller, we check if the user is of a particular type: Guest, Signed-In or Admin and also, if the user is allowed to access this action based on the type. If all the conditions are met, then the action is executed. And this happens in majority of the actions in majority of the controllers.
My thinking
I know this code is smelly because checking if the user is of a particular type and (s)he has access to an action is not the action's responsibility.
My solution which may or may not be possible
We can make a SessionsController (or some other name) and let it handle the authentication and authorization part. But I want the SessionsController to do its job automatically before every request. i.e. Every request should go through the SessionsController and then this controller will decide whether or not to forward the request to the appropriate controller.
I search Google for this but didn't find anything. So my logical conclusion is that passing a request through a series of controllers might not be possible. But I'm not sure. So if it is possible, guide me how to do it. And if it is not possible, then suggest any other way to do it.
This sounds like a perfect example in which one or multiple before_action can be used. You can place a before_action in your ApplicationController:
# in app/controllers/application_controller.rb
private
def authorize_admin
render status: 401 unless current_user? && current_user.admin?
end
Then you can declare in any controller in which you want to run this method before running any action.
# in any controller - even the ApplicationController
before_action :authenticate
You can configure before_action to only run on certain conditions or with certain actions. Just a have a look at the how to use Filters in the Rails Guides.

Multi-tenant Rails app and restricting access to controllers

I am using Devise for authentication, and I have "modules" in my rails app. I am trying to figure out the best approach to security. For example, here are the few things that I want to accomplish:
I want the application controller to require login, unless they are accessing the registrations controller, in which case they are just submitting registration details.
On the application level, I would like to define scopes, permitting access to certain controllers that the user's company has access to.
Additionally, on each request, I want to verify that any IDs in the URL (whether it's a GET, POST, whatever), the user's company has access to that controller and ID in the parameter. (So they can't access Report ID 9 if their company doesn't have a report ID 9 associated with it)
I feel like this may be scalable, but I've never done this before so I'm not quite sure.
Bullet 1
In the ApplicationController, I would like to do something like this:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_user! unless controller == "Registrations"
end
I tried placing a binding.pry in the application controller, but the controller_path seems to always be application. Not sure if there's an easy way to accomplish this without going to each individual controller, which I'm hoping to avoid because I don't want to accidentally forget something when I add a new controller down the road (basically going against DRY).
Maybe I can implement a security controller and have every controller inherit from it? Never did this before but this might work if I can't accomplish what I'm trying to do in the Application Controller.
Bullet 2
I have tried to access Devise's current_user variable from the Application Controller, but it does not exist, so I'm not sure if I can check the user's permissions from the ApplicationController. Again, I'd love to avoid having to place this check in each controller because as the application expands, I may eventually forget to implement checks.
EDIT
So it looks like I have bullet 1 addressed, but now I'm trying to figure out the other 2. Being able to implement some type of "scope" or permission module at the application level. Any ideas?
The skip_before_action directive can suppress execution of a before_action filter that's already defined. You can use this to turn off an action for a controller:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
end
class RegistrationsController < ApplicationController
skip_before_action :authenticate_user!
end
The thing to note here is Ruby is a highly dynamic programming language and code can be executed while the class is being defined. It's important to pay close attention to when certain statements are run, as things like unless, the statement, tend to run immediately when in that context.
You'll see some others that allow deferred execution like in ActiveRecord with:
validates :name, unless: :anonymous?
Where that validates method has a specific option called unless which is distinct from the keyword unless. That defines a validation trigger with a condition attached to it.
On the other hand this code, while visually similar, is completely different:
validates :name unless anonymous?
This depends on a method called anonymous? being available at the class level and if it returns a non nil or false value will execute the validates function.

Ruby saving "Model.all" into application-wide variable

I have a model called Business, and I want to save Business.all into a variable I can access from another part of my Rails application. What is the best way to do this? I am fairly new to Ruby/Ruby on Rails and I know of class and instance variables but I am a bit cloudy on this.
Thanks!
If you want this to be accessible throughout the entire application, you could put it into the application controller found in app/controllers/application_controller.rb.
Example:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :find_all_businesses
def find_all_businesses
#businesses = Business.all
end
end
Hope this helps.
You probably want to look at Rails caching. There is a good screencast here:
http://railscasts.com/episodes/115-caching-in-rails-2-1
It also applies to rails 3. This will allow you to expire the cache if a business is added for example.

geocode_ip_address except specific controllers

I have added geocode_ip_address in ApplicationController so I can get the user location info from their session.
I have some controllers that I don't want them to be checked against Geokit. It really slows application and there's no need for geo check there.
Since its called like geocode_ip_address and not as a before_filter I'm not able to use skip_before_filter
Any idea?
it actually uses store_ip_location filter so you can put
skip_before_filter :store_ip_location
That being said it stores the result of the geo code inside a cookie that is being checked before making a service call so the subsequent calls should not impact performance that much
You can selectively NOT INVOKE the code based on the controller you're in like so:
unless ["controller1", "controller2", "etc"].member?(params[:controller])
geocode_ip_address
end
Put the names of the controllers you don't want the code to run for in the list and it won't be invoked in those controllers.
You could also create a constant that's a list of controllers like this:
CONTROLLERS_TO_NOT_FILTER_BY_GEOCODE = ["controller1", "controller2", "etc"]
unless CONTROLLERS_TO_NOT_FILTER_BY_GEOCODE.member?(params[:controller])
geocode_ip_address
end

How to keep a variable handy across a number of controllers

I've moved an application to using ActiveResource and I'm finding that I need to rethink the way I've taught myself to do some things, and I'm searching for the best way to go about this.
For example, I need to keep a query handy say #current_account which I've done as
#current_account ||= Account.where(etc etc)
in an applicationcontroller.rb for a certain scope. This isn't all that useful with AR, because the call to the API is made each time. I'd like to minimize calls to the api (especially where I have other more expensive calls I don't want run on every query, I want to run them once and keep them handy)
So, what is the Rails way? I have to keep a variable with an AR call to an API handy from the ApplicationController in a certain scope, across several other controllers without having to write it out each time (or call the API each time, or put it in a user accesible session because it isn't exactly text/strings, it is objects I need to use).
I'm curious about how others do this, if I should or should not be doing this, what is the right DRY way, etc. So this is somewhat open-ended.
Any input appreciated.
It's best to create a module for this kind of behavior:
module CustomAuth
def self.included(controller)
controller.send :helper_method, :current_account, :logged_in?
end
def current_account
# note the Rails.cache.fetch. First time, it will
# make a query, but it caches the result and not
# run the query a second time.
#current_account ||= Rails.cache.fetch(session[:account_id], Account.where(...))
end
def logged_in?
!current_account.nil?
end
end
Then, make sure that Rails loads up this file (I put mine in ./lib/custom_auth.rb), so add that to the config.autoload_paths in ./config/application.rb:
# ./config/application.rb
...
config.autoload_path += %W(#{config.root}/lib)
...
Import the CustomAuth module into your application_controller.rb:
class ApplicationController < ActionController::Base
include CustomAuth
protect_from_forgery
...
end
Finally, Crucial: Restart your server
NOTE: You can add additional methods to the custom_auth.rb. If you restart the server, they will be available. These methods are also available in the view, so you can call current_account.name right inside a view.

Resources