我知道Rails观察者不应该直接访问控制器。这是有道理的,没有人知道观察者将从哪个上下文调用。但是我有一个案例,我认为两者之间的间接沟通是值得的,我想知道如何实现它。
记录和编写分析事件
我想使用观察者触发Google Analytics中的某些事件。目前的工作方式是应用程序控制器有一个记录事件的方法,然后application.html.erb
模板将相关的javascript打印到页面中:
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>
示例事件:
class UsersController < ApplicationController
...
def create
...
if @new_user
logGAEvent('user', 'signup')
end
end
end
为什么我想在观察者和控制者之间进行交流
在某些值得注意的事件(有人注册,创建新的配置文件等)之后,在控制器中调用logGAEvent方法。
将大多数这些事件抽象为观察者将是一种更好的模式。这样可以整理控制器,也可以减少跟踪。但是,一旦他们进入观察者,模板仍然需要一种方法来访问观察者的数据并将其打印出来。
我希望能做什么
由于观察者不应该真正了解控制器,我想要做的是将这些事件记录到一次性缓冲区中,以便在每次调用结束时刷新它们,但控制器也可以访问它们写入文件:
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(使用缓冲区)
Application.html.erb
<html>
<head>
...
<script type="text/javascript">
<%= print_ga_events_js(request.buffer.ga_events) %>
</script>
</head>
...
</html>
这在某种程度上是可能的吗?它对我来说似乎不是一种不合理的设计模式,它会使应用程序更清晰。
答案 0 :(得分:3)
我在向应用程序添加mixpanel事件时遇到了类似的问题。我解决它的方式是我使用Controller过滤器而不是观察者。您对观察者的行为非常相似,但使用过滤器实际上可以访问请求和响应对象。
这是一个示例过滤器,从我的松散改编,但有足够的变化,我不会称之为测试代码:
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
在过滤器中,我只是设置了自定义闪存:flash[:mixpanel] ||= []
。 Flash很不错,因为它会自动清除请求之间的内容。我碰到了几个陷阱:
答案 1 :(得分:0)
这有点违背模型 - 视图 - 控制器原理吗?观察者属于该模型,因此无法访问会话/请求/等。 但是,您可以在模型中创建一个实例变量,该变量存储GA事件,并将所有业务逻辑整齐地封装在观察者中,然后在控制器中检索它...
e.g。
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>