How to communicate between observer and controller

I understand that the Rails observer should not have direct access to the controller. This makes sense; there is no information about the context from which the observer will be called. However, I have a case that, it seems to me, deserves an indirect connection between them, and I wonder how to achieve it.

Logging and recording analytics events

I would like to use an observer to trigger certain events in Google Analytics. The way that currently works is that the application controller has a method that logs the event, and then the application.html.erb template prints the appropriate javascript on the page:

 class ApplicationController < ActionController::Base def logGAEvent category, action, opt_hash={} event = { :category => category, :action => action, :label => opt_hash[:label], :value => opt_hash[:value]} (session[:ga_events] ||= []) << event end end 

Application.html.erb

 <html> <head> ... <script type="text/javascript"> <%= print_ga_events_js %> </script> </head> ... </html> 

Event example:

 class UsersController < ApplicationController ... def create ... if @new_user logGAEvent('user', 'signup') end end end 

Why I would like to communicate between the observer and the controller

Currently, the logGAEvent method is called in controllers after certain noteworthy events (someone signs up, creates a new profile, etc.).

It would be much better to draw most of these events in an observer. This will remove the controller and also make tracking less ad-hoc. However, once they enter the observer, there should still be a way for the template to access the observer data and print it.

What i would like to do

Since the observer does not need to know about the controller, I would like to write these events to a one-time buffer so that they are discarded at the end of each call, but they are also available to the controller for writing to the document:

 class UserObserver < ActiveRecord::Observer after_create user # I have no idea what would constitue request.buffer but this is # the pattern I'm looking for request.buffer.ga_events << createGAEvent('user', 'create') end end end 

application.html.erb (using buffer)

Application.html.erb

 <html> <head> ... <script type="text/javascript"> <%= print_ga_events_js(request.buffer.ga_events) %> </script> </head> ... </html> 

Is it possible? This is not like an unreasonable design template for me, and it will make the application cleaner.

+6
source share
2 answers

I had a similar problem with adding mixpanel events to the application. As I decided, I used Controller filters instead of observers. You get very similar behaviors to the observer, but with a filter on which you have access to request and response objects.

Here is an example of a filter, poorly adapted from mine, but with sufficient changes, I would not call it verified code:

 module GoogleAnalytics class TrackCreation def self.for(*args) self.new(*args) end # pass in the models we want to track creation of def initialize(*args) @models = args.map do |name| name.to_s.tableize.singularize end end # This is called after the create action has happened at the controller level def filter(controller) # controller.params.select{ |model, model_params| filter_for.include? model }.each do |model, model_params| #you may want to perform some sort of validation that the creation was successful - say check the response code track_event(model, "create", model_params) end end def track_event(category, action, *args) flash[:ga] ||= [] flash[:ga] << ["_trackEvent", type, args] end def filter_for @models end end end class ApplicationController < ActionController::Base after_filter GoogleAnalytics::TrackCreation.for(:foo, :bar), :only => :create end 

Inside the filter, I just installed a custom flash: flash[:mixpanel] ||= [] . A flash is good because it automatically clears the contents between requests. There are a couple of issues I encountered:

  • Keep in mind the difference between flash and flash.now . A flash is great when you track a created, destroyed resource or any other situation in which a redirect occurs. But if you really want to track the event in an immediate response, you will want to use flash.now.
  • After_filter has access to the answer, but you cannot change this answer. Therefore, if you want to spit out tracking events for the current request, you need to do this before the response is generated.

To really display the analytics code itself, simply update your flash part to take each element in flash [: ga] and write it.

+3
source

Is this not so against the principle of model-view-controller? The observer belongs to the model, so he cannot have access to the session / request / etc. However, you can create an instance variable in a model that stores GA events with all the business logic neatly encapsulated in an observer, and then get it in the controller ...

eg.

 class User < ActiveRecord::Base attr_accessor :ga_events @ga_events=[] #rest of User class end class UserObserver < ActiveRecord::Observer def after_create(user) user.ga_events << createGAEvent('user', 'create') end end class UsersController < ApplicationController ... def create ... if @new_user @ ga_events=@user.ga _events end end end <html> <head> ... <script type="text/javascript"> <%= print_ga_events_js(@ga_events) %> </script> </head> ... </html> 

What do you think?:)

0
source

Source: https://habr.com/ru/post/897521/


All Articles