-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Zen
services are initialized with attributes. To specify list of available attributes, use attributes
class method. All attributes are optional during service initialization. It is possible to omit keys during
initialization, and pass attributes as parameters - in this case attributes will be filled in correspondance
to the order they were defined. However, you cannot pass more attributes than declared attributes list, as
well as cannot pass single attribute multiple times (as parameter and as named attribute) or attributes that
were not declared with attributes
class method.
class MyService < Zen::Service
attributes :foo, :bar
def execute!
# do something
end
def foo
super || 5
end
end
s1 = MyService.new
s1.foo # => 5
s1.bar # => nil
s2 = MyService.new(6, bar: 2)
s2.foo # => 6
s2.bar # => 2
To override attributes for an instantiated service, use #with_attributes
method. This methods return an unexecuted copy of original service:
class MyService < Zen::Service
attributes :foo, :bar
end
s1 = MyService.new(6, bar: 2)
s1.foo # => 6
s1.bar # => 2
s2 = s2.with_attributes(foo: 1, bar: 3)
s2.foo # => 1
s2.bar # => 3
The main purpose of every service object is to be executed. When service is executed, it calls execute!
method which you should define, and returns self
. Upon execution service has result
and responds to success?
method, which tells how did execution go. Usually, successful execution means presence of truthy result. Some usage examples are as following
- Explicitly set execution result:
class Users::Create < Zen::Service
attributes :params
def execute!
result { User.create(params }
send_invitation_email if success?
end
end
service = Users::Create.new(params).execute
command.result # => instance of User
command.success? # => true
- When
result
method is not used, return value ofexecute!
method is used as result. If the result has falsy value, execution will be considered as failure:
class Users::Update < Zen::Service
attributes :user, :params
delegate :errors, to: :user
def execute!
user.update(params)
end
end
service = Users::Update.new(user, params: params).execute
service.result # => instance of User
service.success? # => true
other_service = Users::Update.new(user, params: invalid_params).execute
other_service.result # => false
other_service.success? # => false
- set only execution state by
success!
andfailure!
methods with no result:
class MyService < Zen::Service
def execute!
success!
do_something_that_returns_nil
end
end
service = MyService.new.execute
command.success? # => true
You can use ~
method to delegate execution to another service, adopting it's execution state:
class Posts::Publish < Zen::Service
attributes :post, :publisher
def execute!
post.update(published_by: publisher, published_at: Time.current)
end
end
module Admin
class Posts::Publish < Zen::Service
use :context
attributes :post
delegate :current_admin, to: :context
def execute!
~::Posts::Publish.(post, publisher: current_admin)
end
end
end
-
execute(*)
- executes a service and returns service itself, thus making possible expressions likeif service.execute.success?
. Internally, calls#execute!
method. May be overloaded by plugins to provide additional options or modified behavior, but should always return command instance itself. -
executed?
- returnstrue
if service has already been executed. -
result
- used as reader and writer. When called with no block returns current serviceresult
. When block is given, yields and assigns return value toresult
. -
success?
- returnstrue
if service has been successfully executed. -
failure?
- returns!success?
-
success!(**opts) - sets execution status to "success", even if no result is present. Optional
opts` can be used by plugins to provide additional behavior. -
failure!(**opts)
- same assuccess!
, but sets execution state to "failure". -
with_attributes(attributes)
- returns an unexecuted copy of the command withattributes
replaced by passedattributes
-
.attributes(*attributes_list)
- used to specify list of attributes that can be passed for service instantiation. Passing extra attributes will result inArgumentError
. Also defines corresponding reader methods. -
.call(*attributes)
- instantiates a service withattributes
and executes it immediately:
service = MyService.(:foo, bar: :baz) # => is the same as:
# service = MyService.new(:foo, bar: :baz).execute
-
[](*args)
- instantiates a service withargs
, executes it and returns it's execution result:
result = MyService[:foo, bar: :baz] # => is the same as
# result = MyService.new(:foo, bar: :baz).execute.result