Class: SmsCarrier::Base

Inherits:
AbstractController::Base
  • Object
show all
Includes:
AbstractController::AssetPaths, AbstractController::Callbacks, AbstractController::Helpers, AbstractController::Logger, AbstractController::Rendering, AbstractController::Translation, ActionView::Layouts, DeliveryMethods
Defined in:
lib/sms_carrier/base.rb

Direct Known Subclasses

ApplicationCarrier

Defined Under Namespace

Classes: NullMessage

Constant Summary collapse

PROTECTED_IVARS =
AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DeliveryMethods

#wrap_delivery_behavior!

Constructor Details

#initialize(method_name = nil, *args) ⇒ Base

Instantiate a new carrier object. If method_name is not nil, the carrier will be initialized according to the named method. If not, the carrier will remain uninitialized (useful when you only need to invoke the “receive” method, for instance).



147
148
149
150
151
152
# File 'lib/sms_carrier/base.rb', line 147

def initialize(method_name=nil, *args)
  super()
  @_sms_was_called = false
  @_message = Sms.new
  process(method_name, *args) if method_name
end

Class Attribute Details

.carrier_nameObject

Returns the name of current carrier. If this is an anonymous carrier, this method will return anonymous instead.



85
86
87
# File 'lib/sms_carrier/base.rb', line 85

def carrier_name
  @carrier_name ||= anonymous? ? "anonymous" : name.underscore
end

Class Method Details

.action_methodsObject

Rails newer versions may return an empty Set for abstract controllers. Historically SmsCarrier supported calling ‘SmsCarrier::Base.sms(…)` directly, so we keep `sms` dispatchable only on the Base class itself.



41
42
43
# File 'lib/sms_carrier/base.rb', line 41

def action_methods
  self == SmsCarrier::Base ? super | Set.new(["sms"]) : super
end

.controller_pathObject

Returns the name of current carrier. If this is an anonymous carrier, this method will return anonymous instead.



90
91
92
# File 'lib/sms_carrier/base.rb', line 90

def carrier_name
  @carrier_name ||= anonymous? ? "anonymous" : name.underscore
end

.default(value = nil) ⇒ Object Also known as: default_options=

Sets the defaults through app configuration:

config.sms_carrier.default(from: "+886987654321")

Aliased by ::default_options=



97
98
99
100
# File 'lib/sms_carrier/base.rb', line 97

def default(value = nil)
  self.default_params = default_params.merge(value).freeze if value
  default_params
end

.deliver_sms(sms) ⇒ Object

Wraps an SMS delivery inside of ActiveSupport::Notifications instrumentation.

This method is actually called by the Sms object itself through a callback when you call :deliver on the Sms, calling deliver_sms directly and passing a Sms will do nothing except tell the logger you sent the SMS.



112
113
114
115
116
117
# File 'lib/sms_carrier/base.rb', line 112

def deliver_sms(sms) #:nodoc:
  ActiveSupport::Notifications.instrument("deliver.sms_carrier") do |payload|
    set_payload_for_sms(payload, sms)
    yield # Let Sms do the delivery actions
  end
end

.register_interceptor(interceptor) ⇒ Object

Register an Interceptor which will be called before SMS is sent. Either a class, string or symbol can be passed in as the Interceptor. If a string or symbol is passed in it will be camelized and constantized.



72
73
74
75
76
77
78
79
80
81
# File 'lib/sms_carrier/base.rb', line 72

def register_interceptor(interceptor)
  delivery_interceptor = case interceptor
    when String, Symbol
      interceptor.to_s.camelize.constantize
    else
      interceptor
    end

  Sms.register_interceptor(delivery_interceptor)
end

.register_interceptors(*interceptors) ⇒ Object

Register one or more Interceptors which will be called before SMS is sent.



51
52
53
# File 'lib/sms_carrier/base.rb', line 51

def register_interceptors(*interceptors)
  interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
end

.register_observer(observer) ⇒ Object

Register an Observer which will be notified when SMS is delivered. Either a class, string or symbol can be passed in as the Observer. If a string or symbol is passed in it will be camelized and constantized.



58
59
60
61
62
63
64
65
66
67
# File 'lib/sms_carrier/base.rb', line 58

def register_observer(observer)
  delivery_observer = case observer
    when String, Symbol
      observer.to_s.camelize.constantize
    else
      observer
    end

  Sms.register_observer(delivery_observer)
end

.register_observers(*observers) ⇒ Object

Register one or more Observers which will be notified when SMS is delivered.



46
47
48
# File 'lib/sms_carrier/base.rb', line 46

def register_observers(*observers)
  observers.flatten.compact.each { |observer| register_observer(observer) }
end

.respond_to?(method, include_private = false) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


119
120
121
# File 'lib/sms_carrier/base.rb', line 119

def respond_to?(method, include_private = false) #:nodoc:
  super || action_methods.include?(method.to_s)
end

.supports_path?Boolean

SMS do not support relative path links.

Returns:

  • (Boolean)


291
292
293
# File 'lib/sms_carrier/base.rb', line 291

def self.supports_path?
  false
end

Instance Method Details

#_protected_ivarsObject

:nodoc:



28
29
30
# File 'lib/sms_carrier/base.rb', line 28

def _protected_ivars # :nodoc:
  PROTECTED_IVARS
end

#carrier_nameObject

Returns the name of the carrier object.



179
180
181
# File 'lib/sms_carrier/base.rb', line 179

def carrier_name
  self.class.carrier_name
end

#options(args = nil) ⇒ Object

Allows you to pass random and unusual options to the new SmsCarrier::Sms object which will add them to itself.

options['X-Special-Domain-Specific-Option'] = "SecretValue"

The resulting SmsCarrier::Sms will have the following in its option:

X-Special-Domain-Specific-Option: SecretValue

def options

@_message.options

end



195
196
197
198
199
200
201
# File 'lib/sms_carrier/base.rb', line 195

def options(args = nil)
  if args
    @_message.options.merge!(args)
  else
    @_message
  end
end

#process(method_name, *args) ⇒ Object

:nodoc:



154
155
156
157
158
159
160
161
162
163
164
# File 'lib/sms_carrier/base.rb', line 154

def process(method_name, *args) #:nodoc:
  payload = {
    carrier: self.class.name,
    action: method_name
  }

  ActiveSupport::Notifications.instrument("process.sms_carrier", payload) do
    super
    @_message = NullMessage.new unless @_sms_was_called
  end
end

#response(options) ⇒ Object

:nodoc:



278
279
280
281
282
283
284
285
286
287
288
# File 'lib/sms_carrier/base.rb', line 278

def response(options) #:nodoc:
  if options[:body]
    return options.delete(:body)
  else
    templates_path = options.delete(:template_path) || self.class.carrier_name
    templates_name = options.delete(:template_name) || action_name

    template = lookup_context.find(templates_name, templates_path)
    return render(template: template)
  end
end

#sms(options = {}) ⇒ Object

The main method that creates the message and renders the SMS templates. There are two ways to call this method, with a block, or without a block.

It accepts a headers hash. This hash allows you to specify the most used headers in an SMS message, these are:

  • :to - Who the message is destined for, can be a string of addresses, or an array of addresses.

  • :from - Who the message is from

You can set default values for any of the above headers (except :date) by using the ::default class method:

class Notifier < SmsCarrier::Base
  default from: '+886987654321'
end

If you do not pass a block to the sms method, it will find all templates in the view paths using by default the carrier name and the method name that it is being called from, it will then create parts for each of these templates intelligently, making educated guesses on correct content type and sequence, and return a fully prepared Sms ready to call :deliver on to send.

For example:

class Notifier < SmsCarrier::Base
  default from: 'no-reply@test.lindsaar.net'

  def welcome
    sms(to: 'mikel@test.lindsaar.net')
  end
end

Will look for all templates at “app/views/notifier” with name “welcome”. If no welcome template exists, it will raise an ActionView::MissingTemplate error.

However, those can be customized:

sms(template_path: 'notifications', template_name: 'another')

And now it will look for all templates at “app/views/notifications” with name “another”.

You can even render plain text directly without using a template:

sms(to: '+886987654321', body: 'Hello Mikel!')


250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/sms_carrier/base.rb', line 250

def sms(options = {})
  return @_message if @_sms_was_called && options.blank?

  m = @_message

  # Call all the procs (if any)
  default_values = {}
  self.class.default.each do |k,v|
    default_values[k] = v.is_a?(Proc) ? instance_eval(&v) : v
  end

  # Handle defaults
  options = options.reverse_merge(default_values)

  # Set configure delivery behavior
  wrap_delivery_behavior!(options.delete(:delivery_method), options.delete(:delivery_method_options))

  # Assign all options except body, template_name, and template_path
  assignable = options.except(:body, :template_name, :template_path)
  assignable.each { |k, v| m[k] = v }

  # Render the templates and blocks
  m.body = response(options)
  @_sms_was_called = true

  m
end