有两个这样的课程:
class Site < ActiveRecord::Base
has_one :subscription, dependent: :destroy
def self.hostname_active?(hostname)
site = where(hostname: hostname)
site.exists? && site.first.active?
end
def active?
subscription.active?
end
end
class Subscription < ActiveRecord::Base
belongs_to :site
def active?
(starts_at..ends_at).cover?(Date.current)
end
end
describe Site do
let(:site) { Fabricate.build(:site) }
describe "#hostname_active?" do
it "Returns true if site with hostname exists & is active" do
described_class.stub_chain(:where, :exists?).and_return(true)
described_class.stub_chain(:where, :first) { site }
site.stub(:active?).and_return(true)
described_class.hostname_active?('www.example.com').should be_true
end
it "Returns false if site with hostname doesn't exist" do
described_class.stub_chain(:where, :exists?).and_return(false)
described_class.stub_chain(:where, :first) { site }
site.stub(:active?).and_return(false)
described_class.hostname_active?('www.example.com').should be_false
end
it "Returns false if site is not active" do
described_class.stub_chain(:where, :exists?).and_return(true)
described_class.stub_chain(:where, :first) { site }
site.stub(:active?).and_return(false)
described_class.hostname_active?('www.example.com').should be_false
end
end
end
如果相关订阅确定站点是否处于活动状态,我使用方法hostname_active?
作为路由中的约束以及我需要确定a)是否存在的其他类,以及b)很活跃。
取自另一个关于SO的问题:
Tell-not-ask基本上意味着你不应该查询关于它状态的对象,根据它的状态做出决定,然后告诉同一个对象要做什么。如果对象具有所需的所有信息,则应自行决定。
虽然我不这样做,但我的代码确实感觉非常耦合,无论是在网站和网站之间的耦合方面。订阅,但也与ActiveRecord的耦合,这使得很难在不触及数据库的情况下进行测试。
如何构建它以避免询问相关订阅以确定网站的状态?而且,你会认为这违反了告诉不要问吗?
答案 0 :(得分:1)
使用ActiveRecord,你会有一些耦合,而且没关系,而你正在做的事情并没有违反LOD。您可以对active
字段进行非规范化以将其保留在网站本身上,但我不会这样做。
我要改变的一件事是急于加载订阅。
#Just showing changes
class Site < ActiveRecord::Base
scope :active, includes(:subscription).merge(Subscription.active)
has_one :subscription, dependent: :destroy
def self.hostname_active?(hostname)
active.where(hostname: hostname).exists?
end
end
class Subscription < ActiveRecord::Base
scope :active, where(arel_table[:starts_at].lteq(Date.current), arel_table[:ends_at].gteq(Date.current))
end
至少,这将使您不必进行两次查询以确定主机名是否处于活动状态。
就存根ActiveRecord而言,我通常不会发现这种情况。普遍接受的做法是使用夹具或工厂来构建测试对象。就个人而言,我使用FactoryGirl:https://github.com/thoughtbot/factory_girl_rails。
在你的情况下,我会有像:
这样的工厂FactoryGirl.define do
factory :subscription do
site
factory :active_subscription do
starts_at { Date.today.beginning_of_month }
ends_at { Date.today.end_of_month }
end
factory :inactive_subscription do
starts_at { Date.today.beginning_of_month - 3.months }
ends_at { Date.today.end_of_month - 3.months }
end
end
end
FactoryGirl.define do
factory :site do
sequence(:hostname, 1000) {|h| "site#{h}.example.com" }
factory :active_site do
after(:create) do |site|
FactoryGirl.create(:active_subscription, site: site)
end
end
factory :inactive_site do
after(:create) do |site|
FactoryGirl.create(:inactive_subscription, site: site)
end
end
end
end
这样我的规格就可以了:
describe Site do
describe "Active site" do
subject(:site) { FactoryGirl.create :active_site }
its(:active?) { should eq(true) }
end
#etc...
end