Rails 中的关注点分离困境
我正在尝试为我的 Rails 应用程序进行日志记录,但对 Rails 中使用的哲学存在一些困境。我的应用程序有 Link
模型,其中 has_many
Hit
s:
class Link < AR::Base
has_many :hits
end
class Hit < AR::Base
belongs_to :link
end
现在每次点击链接时,我都会调用 hit!
在链接上记录请求的方法(为了保持控制器瘦身,我使模型变胖):
class LinksController < ApplicationController
def hit
link = Link.find(params[:id])
link.hit!(request)
end
end
class Link < AR::Base
def hit!(request)
params = extract_data_from_request(request)
hits.create(params)
end
end
现在这就是我感到困惑的地方。我想记录 request
对象附带的数据(如远程 IP、引用者、用户代理等),因此我需要将请求对象传递给模型,但我认为这不符合到“关注点分离”并模糊了 MVC 设计模式中的责任线(当然,如果我错了,请纠正我)。另外,如果我在控制器本身中创建一个 Hit
对象,那么我将制作瘦模型和胖控制器:
class LinksController < ApplicationController
def hit
hit_params = extract_data_from_request(request)
Hit.create(hit_params.merge(:link_id => params[:id])
end
end
尽管后一种情况使测试变得更容易(我不需要在模型中模拟请求)规格)-这似乎不太正确。
对此有任何建议 - 非常感谢。
PS extract_data_from_request(req)
方法放置在需要的适当位置。它返回 Hit
对象所需属性的哈希值。
I'm trying to make logging for my rails app and have some dilemmas over philosophy used in rails. My app has Link
model which has_many
Hit
s:
class Link < AR::Base
has_many :hits
end
class Hit < AR::Base
belongs_to :link
end
Now each time the link is being hit, I call hit!
method to record a request on the link (to keep the controller skinny I make the model fat):
class LinksController < ApplicationController
def hit
link = Link.find(params[:id])
link.hit!(request)
end
end
class Link < AR::Base
def hit!(request)
params = extract_data_from_request(request)
hits.create(params)
end
end
Now here's where I'm confused. I want to record the data that came with request
object (like remote ip, referrer, user agent, etc.) so I need to pass request object down to model, but I believe that this does not conform to "separation of concerns" and blurs responsibility lines in MVC design pattern (of course, correct me if I'm wrong). Also if I'll create a Hit
object in controller itself, then I'm making skinny model and fat controller:
class LinksController < ApplicationController
def hit
hit_params = extract_data_from_request(request)
Hit.create(hit_params.merge(:link_id => params[:id])
end
end
Although the latter case makes testing much easier (I don't need to mock request in model specs) - it just doesn't seem right.
Any advice on this - much appreciated.
P.S. extract_data_from_request(req)
method is placed in appropriate places where needed. It returns a hash of needed attributes for Hit
object.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
就我个人而言,我会警惕过度思考这些事情。
命中的概念与网站或 Web 应用程序密切相关,(HTTP)请求的概念也是如此。胖控制器反模式更多地涉及包含 ActiveRecord 查找语句和业务逻辑的冗长控制器操作(通常以
if
/elsif
/else
块)可以轻松提取到模型中。控制器具有一定的编排职责。在一个人体内创造一个物体并不是一种令人发指的罪行。毕竟,我们一直在
create
操作中执行此操作。Personally I would be wary of over-thinking these things too much.
The concept of a hit is very much tied to a website or web application, as is the concept of a (HTTP) request. The fat controller anti-pattern is more about having lengthy controller actions that contain ActiveRecord find statements and business logic (often characterised by
if
/elsif
/else
blocks) that can easily be extracted into the model.Controllers have certain orchestration responsibilities. Creating an object within one isn't a heinous crime. After all, we do it all the time in our
create
actions.是的,我同意约翰的观点。请求的概念通常是“控制器事物”,但在这种情况下,您的模型是对请求进行建模,因此在这种情况下它绝对属于模型领域。实际上,一旦请求对象跨越了从控制器到模型的边界,它就只是另一个对象,没有特殊的属性:它不再关心获取和响应 html 请求的过程,它只是一个你可以做任何你想做的事情的对象。
不过,需要警惕的一件事是,在 ruby 中,参数是通过引用传递的。这意味着您在模型中操作的请求对象与控制器中处理的请求对象是同一对象。我可能过于偏执(或者完全错误),但您可能希望将其副本传递给模型而不是实际请求本身。 IE
Yeah, i'd agree with John. The concept of a request would normally be a 'controller thing' but in this case your model is modelling a request, so it's definitely in model territory in this case. Effectively once the request object crosses the boundary from controller into model it's just another object, with no special properties: it's no longer concerned with the process of getting and responding to html requests, it's just an object that you can do whatever you want with.
One thing to be wary of, though, is that in ruby arguments are passed by reference. That means that the request object you are manipulating in your model is THE SAME OBJECT as the one being dealt with in the controller. I may be being overly paranoid (or just plain wrong) but you might want to pass a duplicate of it to the model rather than the actual request itself. ie