Class: Telephone::Service
- Inherits:
-
Object
- Object
- Telephone::Service
- Includes:
- ActiveModel::Model
- Defined in:
- lib/telephone/service.rb
Instance Attribute Summary collapse
-
#result ⇒ Object
The primary outcome of the service object.
Class Method Summary collapse
-
.argument(arg, default: nil, required: false, type: nil) ⇒ Object
Defines a getter/setter for a service object argument.
-
.call(*args, **kwargs) ⇒ Object
Executes the service object action directly from the class — similar to the way a Proc can be executed with ‘Proc#call`.
-
.defaults ⇒ Object
Used to maintain a list of default values to set prior to initialization based on the options in #argument.
-
.method_added(method_name) ⇒ Object
When the subclass overwrites the #call method, reassign it to #__call.
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(attributes = {}) {|_self| ... } ⇒ Service
constructor
Primary responsibility of initialize is to instantiate the attributes of the service object with the expected values.
-
#success? ⇒ Boolean
Determines whether or not the action of the service object was successful.
Constructor Details
#initialize(attributes = {}) {|_self| ... } ⇒ Service
Primary responsibility of initialize is to instantiate the attributes of the service object with the expected values.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/telephone/service.rb', line 22 def initialize(attributes = {}) attributes = attributes.transform_keys(&:to_sym) attributes.each do |key, value| send("#{key}=", value) end self.class.defaults.each do |key, value| next if attributes.key?(key) resolved = if value.is_a?(Proc) instance_exec(&value) elsif value.respond_to?(:call) value.call else value end send("#{key}=", resolved) end super yield self if block_given? end |
Instance Attribute Details
#result ⇒ Object
The primary outcome of the service object. For consistency, all service objects always return self. If you need a value returned, the return value of #call will be available on the attribute @result.
17 18 19 |
# File 'lib/telephone/service.rb', line 17 def result @result end |
Class Method Details
.argument(arg, default: nil, required: false, type: nil) ⇒ Object
Defines a getter/setter for a service object argument. This also allows you to pass in a default, or set the argument to “required” to add a validation that runs before executing the block.
The default value can be a static value or any callable object (Proc, lambda, method, or any object that responds to #call) that will be evaluated at runtime when the service is instantiated.
Callable defaults are evaluated in the context of the service instance, so they can access other attributes. They are processed in definition order, meaning a callable can depend on any argument defined before it.
To store a Proc as the actual value, wrap it in another lambda:
argument :my_proc, default: -> { -> { puts "hi" } }
You can also pass type: to validate that the argument is an instance of the given class or module. Nil values are allowed unless the argument is also marked as required.
98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/telephone/service.rb', line 98 def argument(arg, default: nil, required: false, type: nil) arg = arg.to_sym send(:attr_accessor, arg) send(:validates, arg, presence: true) if required if type send(:validates_with, Telephone::TypeValidator, attributes: arg, with: type, allow_nil: true) end defaults[arg] = default end |
.call(*args, **kwargs) ⇒ Object
Executes the service object action directly from the class — similar to the way a Proc can be executed with ‘Proc#call`. This allows us to add some common logic around running validations and setting argument defaults.
125 126 127 |
# File 'lib/telephone/service.rb', line 125 def call(*args, **kwargs) new(args..merge(kwargs)).call end |
.defaults ⇒ Object
Used to maintain a list of default values to set prior to initialization based on the options in #argument.
113 114 115 |
# File 'lib/telephone/service.rb', line 113 def defaults @defaults ||= {} end |
.method_added(method_name) ⇒ Object
When the subclass overwrites the #call method, reassign it to #__call. This allows us to still control what happens in the instance level of #call.
132 133 134 135 136 137 138 139 |
# File 'lib/telephone/service.rb', line 132 def method_added(method_name) if method_name == :call alias_method :__call, :call send(:remove_method, :call) else super end end |
Instance Method Details
#call ⇒ Object
59 60 61 62 |
# File 'lib/telephone/service.rb', line 59 def call self.result = __call if valid? self end |
#success? ⇒ Boolean
Determines whether or not the action of the service object was successful.
55 56 57 |
# File 'lib/telephone/service.rb', line 55 def success? errors.empty? end |