观察者和控制器之间如何通信
我知道 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< /strong>
<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>
这是在有可能吗?对我来说,这似乎不是一个不合理的设计模式,它会让应用程序更加干净。
I understand that a Rails observer should not have direct access to the controller. That makes sense, there is no telling what context the observer is going to be called from. However I have a case that I think merits indirect communication between the two and I'm wondering how to achieve it.
logging and writing analytics events
I would like to use an observer to trigger certain events in Google Analytics. The way this currently works is that the application controller has a method that logs the event and then the application.html.erb
template prints the relevant javascript into 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>
Example event:
class UsersController < ApplicationController
...
def create
...
if @new_user
logGAEvent('user', 'signup')
end
end
end
Why I would like to communicate between an observer and the controller
At the moment the logGAEvent method is called in controllers after certain noteworthy events (someone signs up, creates a new profile etc).
It would be a far nicer pattern to abstract the majority of these events into an observer. This would tidy up the controller and would also make the tracking less ad-hoc. However, once they go into the observer, there still needs to be a way for the template to access the observer's data and print it out.
What I would like to be able to do
Since the observer shouldn't really know about the controller, what I would like to do is record these events into a one-time buffer so that they are flushed at the end of each call but they are also accessible to the controller to write into 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 this in some way possible? It doesn't seem like an unreasonable design pattern to me and it would make the app much cleaner.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在向应用程序添加 mixpanel 事件时遇到了类似的问题。我解决这个问题的方法是使用控制器过滤器而不是观察者。您会得到与观察者非常相似的行为,但通过过滤器,您实际上可以访问请求和响应对象。
这是一个示例过滤器,大致改编自我的过滤器,但经过足够的更改,我不会将其称为测试代码:
在过滤器中,我只是设置了一个自定义闪光灯:
flash[:mixpanel] ||= []
。 Flash 很好,因为它会自动清除请求之间的内容。我遇到了一些问题:要实际输出分析代码本身,只需更新您的 flash 部分以获取 flash[:ga] 中的每个元素并将其写出。
I had a similar issue with adding mixpanel events to an application. The way I solved it was I used Controller filters instead of observers. You get very similar sorts of behavior to an observer but with a filter you actually have access to the request and response objects.
Here's an example filter, loosely adapted from mine but with enough changes I wouldn't call it tested code:
Within the filter I just set up a custom flash:
flash[:mixpanel] ||= []
. Flash is nice because it will automagically clear contents between requests. There are a couple gotchas I ran into:To actually output the analytics code itself just update your flash partial to take each element in flash[:ga] and write it out.
这不是有点违反模型-视图-控制器原则吗?观察者属于模型,因此它无法访问会话/请求等。
但是,您可以在模型中创建一个实例变量,用于存储 GA 事件,并将所有业务逻辑整齐地封装在观察者中,然后在控制器中检索该变量...
例如,
Whatchya 认为? :)
Isn't this a bit against the model-view-controller principle? The observer belongs to the model, so it can't have access to the session/request/etc.
However, you could create an instance variable in the model that stores the GA events with all the business logic neatly encapsulated in an observer, and then retrieve that in a controller...
e.g.
Whatchya reckon? :)