class ArchSpec::DSL::ComponentProxy
A handle to one component, returned by ArchSpec::DSL::Context#component and by calling a declared component’s name. Rule methods return self, so they chain.
services.cannot_use(:controllers).cannot_call(:render, receiver: :none)
Attributes
Public Class Methods
Source
# File lib/archspec/dsl.rb, line 158 def initialize(definition, name) @definition = definition @name = name.to_sym end
Public Instance Methods
# File lib/archspec/dsl.rb, line 196 def can_only_be_used_by(*consumers) add_rule(Rules::AllowedConsumersRule.new(name, consumers)) self end
Allowlists the components that may reference this one, the inverse of can_use. A reference from any other component fails. Use it to protect a shared kernel or a component with a deliberately narrow audience.
shared_kernel.can_only_be_used_by :billing, :catalog
Rule id: dependencies.consumers.
Source
# File lib/archspec/dsl.rb, line 170 def can_use(*targets) add_rule(Rules::AllowDependenciesRule.new(name, targets)) self end
Allowlists the components this one may depend on. A reference to any other declared component fails.
controllers.can_use :models, :services
only_depend_on and must_only_depend_on are aliases. Rule id: dependencies.allow.
# File lib/archspec/dsl.rb, line 212 def cannot_call(*methods, receiver: :any) add_rule(Rules::CannotCallRule.new(name, methods, receiver: receiver)) self end
Forbids calling the named methods. By default any receiver matches, so this catches record.update and cache.update alike. Pass receiver: :none to match only bare, implicit-self calls, which is how the Rails presets keep the controller API out of models.
queries.cannot_call :save, :update, :destroy services.cannot_call :render, :params, receiver: :none
A bare call to a method the component defines, inherits, or generates with attr_* or delegate is treated as its own API and not flagged. Rule id: methods.forbid.
Source
# File lib/archspec/dsl.rb, line 224 def cannot_define(*methods) add_rule(Rules::CannotDefineMethodRule.new(name, methods)) self end
Forbids defining the named methods in this component. Use it when the method name itself is a design smell there, such as call on a component that should not hold command objects.
models.cannot_define :call
Rule id: methods.define_forbid.
# File lib/archspec/dsl.rb, line 234 def cannot_instantiate_and_invoke add_rule(Rules::CannotInstantiateAndInvokeRule.new(name)) self end
Forbids the one-shot Thing.new(...).call pattern, where a class is instantiated and immediately invoked. Use it to steer a component toward plain methods over anonymous command objects.
Rule id: objects.instantiate_and_invoke_forbid.
# File lib/archspec/dsl.rb, line 245 def cannot_reference_constants(*constants) add_rule(Rules::CannotReferenceConstantsRule.new(name, constants)) self end
Forbids referencing the named constants or anything under them. Use this when the boundary is a framework constant rather than a component.
models.cannot_reference_constants "ActionController", "ActionView"
Rule id: constants.forbid.
# File lib/archspec/dsl.rb, line 273 def cannot_reference_includers add_rule(Rules::ConcernIndependenceRule.new(name)) self end
Forbids a concern from referencing the constants that include it. A concern that names its includer knows too much about who uses it, which couples the two and defeats the point of extracting the concern.
component :model_concerns, in: "app/models/concerns/**/*.rb" model_concerns.cannot_reference_includers
Rule id: concerns.independence.
Source
# File lib/archspec/dsl.rb, line 184 def cannot_use(*targets) add_rule(Rules::ForbidDependenciesRule.new(name, targets)) self end
Forbids depending on the named components. Narrower than can_use: only the listed components fail, other dependencies are left alone.
models.cannot_use :controllers, :helpers
Rule id: dependencies.forbid.
Source
# File lib/archspec/dsl.rb, line 330 def methods(scope: :instance) Rules::Naming::Builder.new(self, scope: scope) end
Starts a naming-convention rule over the component’s defined, public methods. Select the methods with matching, then assert something about them. Every check is name-based and exact.
models.methods.matching(/\A(get|set)_/).forbidden chat.methods.matching(/\Awith_(?<base>.+)/).requires("without_%{base}") chat.methods.matching(/\Awith_(?<b>.+)/).requires("%{b}", on: agent, scope: :class)
Pass scope: :class to select class methods instead of instance methods. See ArchSpec::Rules::Naming::Selected for the constraints (forbidden, requires). Rule ids: naming.forbidden, naming.requires.
This shadows +Object#methods+ on the proxy, which is only ever used as a short-lived DSL handle.
Source
# File lib/archspec/dsl.rb, line 286 def must_be_empty(because: nil) add_rule(Rules::MustBeEmptyRule.new(name, because: because)) self end
Requires the component to hold no files. Use it to keep a directory empty, such as app/services in a vanilla Rails app, with a reason shown in the diagnostic.
component(:services, in: "app/services/**/*.rb") .must_be_empty(because: "behavior belongs on models")
Rule id: components.empty.
Source
# File lib/archspec/dsl.rb, line 298 def must_implement(*methods) methods.each do |method_name| add_rule(Rules::MustImplementRule.new(name, method_name)) end self end
Requires every class in the component to implement all the named instance methods. Methods inherited from resolvable superclasses or mixins count.
commands.must_implement :perform
Rule id: protocol.must_implement.
# File lib/archspec/dsl.rb, line 311 def must_implement_one_of(*methods) add_rule(Rules::MustImplementOneOfRule.new(name, methods)) self end
Requires every class in the component to implement at least one of the named instance methods. Useful when a protocol allows either name.
commands.must_implement_one_of :perform, :call
Rule id: protocol.must_implement_one_of.
# File lib/archspec/dsl.rb, line 260 def public_api(*patterns, constants: nil, namespace: nil) add_rule(Rules::PublicApiRule.new(name, files: patterns, constants: constants, namespaces: namespace)) self end
Marks part of the component as its public API. References from outside must resolve to a public constant; everything else becomes private.
billing.public_api "packs/billing/app/public/**/*.rb" billing.public_api constants: "Billing::Api" billing.public_api namespace: "Billing::Public"
constants matches exact names, namespace matches a name and its children. Code inside the component may still reach its own internals. Rule id: dependencies.privacy.