Logging Feature Usage in Rails App - ruby-on-rails

I'm interested in logging information about what features users are using in my Rails app. I know of existing Gems for performance metrics, logging user activity (e.g., public_activity), or more general analytics (e.g., google analytics), but what I'm looking for is a bit difference.
For example, if I have a feature that enables users to export their data, what service would you recommend to keep track of how many users (and which users) are using this feature?

Simple option would of course be to write a another model (for example Activity) that keeps track of the usages:
class Activity < ActiveRecord::Base
# attributes for resource (export, in this case), action type (Excel export) and user id
end
Then it could be applied with before_filter on your ExportController:
ExportController < ApplicationController
before_filter :register_activity
def register_activity
ExportActivity.create { } # Apply the params here
end
end
This way also provides you with the possibility to easily access the data and make statistics about it as you have it under your own control.

Related

How to properly implement quota reached functionality in rails

I was wondering on how to properly and cleanly implement quota reached functionality in my SaaS depending on the plan the workspace is on.
Solution 1 (my first pick):
I was thinking of using the fail early principle and checking the user quota inside of the rails controller.
# ClientsController
before_action :check_quota_limit, only: [:create]
# ApplicationController
rescue_from MySaaS::QuotaLimitReached, with: :quota_limit_reached
Solution 2:
The other solution I was thinking of is doing it inside of the model. But this has a few drawbacks since you can easily bump into N+1 queries while validating + you don't always have the current user available through a model. Which would result in multiple queries just to be able to save this model.
# Client
class Client
validates :quota_limit_reached
end
I'm leaning towards solution 1 but I just want to see if anyone else has something that didn't cross my mind yet.

Incomprehension about the first example in Rails' Guide for action cable

I am trying to add the User Appearances example (from the Rails' Guide : https://guides.rubyonrails.org/action_cable_overview.html#example-1-user-appearances ) in my app but I don't understand this part :
# app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
def subscribed
current_user.appear
end
def unsubscribed
current_user.disappear
end
def appear(data)
current_user.appear(on: data['appearing_on'])
end
def away
current_user.away
end
end
If someone has an explanation for the following sentence : "That appear/disappear API could be backed by Redis, a database, or whatever else." (Just above this part of code in the Rails' Guide).
I try several options, as adding a method "appear" in my model User which change on "true" a database value from my model User, but the subscribed definition call current_user.appear and then the appear definition call current_user.appear(with_param) generates a conflict ...
There is probably something I don't understand but I don't see exactly what is it ...
Thank you very much for your answers.
The sentence about "appear/disappear API backing" - means that ActionCable does not care where and how you are storing and handling users statuses - you may store only a flag or more data in database (like last seen chatroom, last seen time etc.), you may store similar data in redis or any other place you like.
(un)subscribed methods are caller by ActionCable itself upon user (dis)connection to that channel(usually this happens on page load and after navigating away/closing - and while page is open in browser it does not necessary mean that user is actually near their device), while appear/away are actions that are called from clientside js via calling perform("action_name_here") on the channel.
Example assumes that clientside code will detect user presence and send updates.

Too many sessions in a rails application

I am building an E-commerce website on ruby on rails from scratch.(This is my first project on ruby on rails)
My product belongs to a subcategory which in-turn belongs to a category.
My filters partial include multiple check-boxes for category,subcategory,additional_category(Like hand made clothes,factory built etc.),lifestyle(relaxed,corporate etc) and cloth_material_type(this has around 30 options)
I am sending 5 arrays for each of these cases to the backend to search through the associations.
Now when a non logged in user reloads the page the filters set by user resets to default.
To avoid this I have four options in mind.
Option 1. Store the filter values set by the user in the cookies which is fast.But it might slow down the user's browser.
Option2 . Store the values in a session using ActiveRecord::SessionStore gem which will increase the size of session for me to 65K but would slow down the application.
Option 3 .Using jquery modify/create document.url options so that every filter option gets appended to the document.url and on reload I get the parameters set by the user for filtering.But this looks very cumbersome to implement.
Option 4. Using gems like rails temporary database etc.
I have opted with option 2 and using session store for the purpose but I think that it will become cumbersome to maintain this in the future.
Just need some suggestions like what do other rails ecommerce websites do to solve this problem or is there any better way to solve this.
Redis
What I'd do is add a layer of abstraction; specifically I think you'd benefit from using Redis, or similar temporary db (as you alluded to in your question).
Redis is a key:value database, which basically stores JSON values for you to use within your app. If you tie it to a model, you'll be able to store temporary values without hindering your app's performance.
I think you could setup Redis to store a guest id, and an array of your values from that:
[guest_user_id] => [
1 => "x"
2 => "y"
3 => ["z", "a", "b"]
]
You'd be able to generate the guest_user_id when you initialize the Redis system, and store it in the user's session. This way, you're only storing minimal data inside your user's browser, and can populate the various controller actions with Redis data:
#config/routes.rb
resources :categories do
resources :subcategories
end
#app/models/user.rb
Class User < ActiveRecord::Base
def self.new_data
# create guest_id and send to Redis
end
end
This will allow you to populate a session with your guest_id if the user is not registered:
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def show
#user = user_signed_in? ? current_user : User.new_data
#You'll then be able to populate their Redis values with the data from the product selection etc
end
end
I could go into more specifics, but as you're only looking for suggestions, this is what I have to recommend at the moment

How to intercept information from a Rails logger?

I'm currently using Rails v2.3.5 at work and I'm quite new both to the language and the framework. What I need to do is to capture certain information every time a user requests a webpage (user's IP address, URL accessed, Date and time of access and the time the page took to render) and store it in a database.
I've noticed that all this information is contained in the default Rails' logfiles, but I'm trying to avoid having to parse the logfile to collect this information. What I would like is some way to hook to the logger and intercept this information or perhaps extend the logger and use my extended version instead of the default Rails one (ActiveSupport::BufferedLogger).
Maybe ever other solutions that don't require logs?
Thanks in advance.
What you probably need is a before_filter block in your ApplicationController to perform whatever action you need to do. From there you can create whatever database records you need. For example:
class ApplicationController < ActionController::Base
before_filter :log_user_info
protected
def log_user_info
# Activate a custom logging method on a model
UserActivity.log!(
# ... User parameters
)
end
end
Hooking in to the logger is probably a bad idea as that's an indirect way to get that information. You may need to extract data from this for historical reasons, though.

Using :attr_accessible with role-based authorization

In my online store, users are allowed to change certain properties of their orders (e.g., their billing address), but not others (e.g., the origination ip address). Administrators, on the other hand, are allowed to modify all order properties.
Given, this, how can I use :attr_accessible to properly secure my Order model? Or will I have to use it to mark accessible all attributes that administrators can modify and refrain from using Order.update_attributes(params[:order]) in those controller actions that ordinary users can access?
Generally speaking, attr_accessible is not the tool you're looking for and Rails doesn't come with anything built in that does what you want.
If you want fine-grained control over who can update specific attributes in a model, you could do something like:
class Order < ActiveRecord::Base
def update_attributes_as_user(values, user)
values.each do |attribute, value|
# Update the attribute if the user is allowed to
#order.send("#{attribute}=", value) if user.can_modify?(attribute)
end
save
end
end
Then you can change your Order.update_attributes(params[:order]) to Order.update_attributes_as_user(params[:order], current_user) and assuming you implement the User#can_modify? method to return true in the correct cases, it should work.
I had the same problem and now I'm using this gem http://github.com/dmitry/attr_accessible_block
It's easy and used in some production website.
Yes, you'll have to modify the actions, so permissions are checked inside the actions. Calling Order#update_attributes will not work for the general user.
I can't rember a role-based authorization plugin that would allow something you are looking for. This is because these plugins mixin to the controllers and not the models. They would also need to mixin into ActiveRecord::Base to check for attr_accesible etc.

Resources