class Mobb::Base

Constants

CALLERS_TO_IGNORE
DEFAULT_CONDITIONS

Attributes

events[R]
filters[R]
matched[R]

Public Class Methods

add_filter(type, pattern = /.*/, **options, &block) click to toggle source
# File lib/mobb/base.rb, line 218
def add_filter(type, pattern = /.*/, **options, &block)
  filters[type] << compile!(type, pattern, options, &block)
end
after(pattern = /.*/, **options, &block) click to toggle source
# File lib/mobb/base.rb, line 214
def after(pattern = /.*/, **options, &block)
  add_filter(:after, pattern, options, &block)
end
before(pattern = /.*/, **options, &block) click to toggle source
# File lib/mobb/base.rb, line 210
def before(pattern = /.*/, **options, &block)
  add_filter(:before, pattern, options, &block)
end
clear(*options) click to toggle source
# File lib/mobb/base.rb, line 388
def clear(*options) options.each { |option| set(option, nil) }; end
compile(pattern, options) click to toggle source
# File lib/mobb/base.rb, line 268
def compile(pattern, options) Matcher.new(pattern, options); end
compile!(type, pattern, options, &block) click to toggle source
# File lib/mobb/base.rb, line 241
def compile!(type, pattern, options, &block)
  at = options.delete(:at)

  defaults = DEFAULT_CONDITIONS[type]
  options = defaults ? defaults.merge(options) : options
  options.each_pair { |option, args| send(option, *args) }

  matcher = case type
            when :message
              compile(pattern, options)
            when :ticker
              compile_cron(pattern, at)
            when :trigger
              compile(pattern, options)
            else
              compile(pattern, options)
            end
  unbound_method = generate_method("#{type}", &block)
  source_conditions, @source_conditions = @source_conditions, []
  dest_conditions, @dest_conditions = @dest_conditions, []
  wrapper = block.arity != 0 ?
    proc { |instance, args| unbound_method.bind(instance).call(*args) } :
    proc { |instance, args| unbound_method.bind(instance).call }

  [ matcher, wrapper, source_conditions, dest_conditions ]
end
compile_cron(time, at) click to toggle source
# File lib/mobb/base.rb, line 270
def compile_cron(time, at)
  if String === time
    Matcher.new(CronParser.new(time))
  else
    Matcher.new(CronParser.new(Whenever::Output::Cron.new(time, nil, at).time_in_cron_syntax))
  end
end
condition(name = " click to toggle source
# File lib/mobb/base.rb, line 339
def condition(name = "#{caller.first[/`.*'/]} condition", &block)
  @source_conditions << generate_method(name, &block)
end
Also aliased as: source_condition
cron(pattern, options = {}, &block) click to toggle source
# File lib/mobb/base.rb, line 225
def cron(pattern, options = {}, &block) event(:ticker, pattern, options, &block); end
Also aliased as: every
dest_condition(name = " click to toggle source
# File lib/mobb/base.rb, line 344
def dest_condition(name = "#{caller.first[/`.*'/]} condition", &block)
  @dest_conditions << generate_method(name) do |res|
    if String === res
      res = [res, {}]
    end
    block.call(res)
    res
  end
end
dest_to(channel) click to toggle source
# File lib/mobb/base.rb, line 380
def dest_to(channel)
  dest_condition do |res|
    res.last[:dest_channel] = channel
  end
end
development?() click to toggle source
# File lib/mobb/base.rb, line 299
def development?; environment == :development; end
disable(*options) click to toggle source
# File lib/mobb/base.rb, line 387
def disable(*options) options.each { |option| set(option, false) }; end
enable(*options) click to toggle source
# File lib/mobb/base.rb, line 386
def enable(*options) options.each { |option| set(option, true) }; end
event(type, pattern, options, &block) click to toggle source
# File lib/mobb/base.rb, line 230
def event(type, pattern, options, &block)
  signature = compile!(type, pattern, options, &block)
  (@events[type] ||= []) << signature
  invoke_hook(:event_added, type, pattern, block)
  signature
end
every(pattern, options = {}, &block)
Alias for: cron
extensions() click to toggle source
# File lib/mobb/base.rb, line 198
def extensions
  if superclass.respond_to?(:extensions)
    (@extensions + superclass.extensions).uniq
  else
    @extensions
  end
end
generate_method(name, &block) click to toggle source
# File lib/mobb/base.rb, line 278
def generate_method(name, &block)
  define_method(name, &block)
  method = instance_method(name)
  remove_method(name)
  method
end
helpers(*extensions, &block) click to toggle source
# File lib/mobb/base.rb, line 285
def helpers(*extensions, &block)
  class_eval(&block)   if block_given?
  include(*extensions) if extensions.any?
end
include_myself(cond) click to toggle source
# File lib/mobb/base.rb, line 361
def include_myself(cond)
  source_condition do
    return true if !@env.respond_to?(:user) || cond
    @env.user.name != settings.name
  end
end
invoke_hook(name, *args) click to toggle source
# File lib/mobb/base.rb, line 237
def invoke_hook(name, *args)
  extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
end
on(pattern, options = {}, &block)
Alias for: receive
production?() click to toggle source
# File lib/mobb/base.rb, line 300
def production?; environment == :production; end
quit!() click to toggle source
# File lib/mobb/base.rb, line 408
def quit!
  return unless running?
  running_service.respond_to?(:stop!) ? running_service.stop! : running_service.stop
  $stderr.puts "== Great sound Mobb, thank you so much"
  clear :running_service, :handler_name
end
react_to_bot(cond) click to toggle source
# File lib/mobb/base.rb, line 354
def react_to_bot(cond)
  source_condition do
    return true if !@env.respond_to?(:bot?) || cond
    !@env.bot?
  end
end
receive(pattern, options = {}, &block) click to toggle source
# File lib/mobb/base.rb, line 222
def receive(pattern, options = {}, &block) event(:message, pattern, options, &block); end
Also aliased as: on
register(*extensions, &block) click to toggle source
# File lib/mobb/base.rb, line 290
def register(*extensions, &block)
  extensions << Module.new(&block) if block_given?
  @extensions += extensions
  extensions.each do |extension|
    extend extension
    extension.registered(self) if extension.respond_to?(:registered)
  end
end
reply_to_me(cond) click to toggle source
# File lib/mobb/base.rb, line 368
def reply_to_me(cond)
  condition do
    @env.reply_to.include?(settings.name) == cond
  end
end
reset!() click to toggle source
# File lib/mobb/base.rb, line 190
def reset!
  @events = {}
  @filters = { before: [], after: [] }
  @source_conditions = []
  @dest_conditions = []
  @extensions = []
end
run!(options = {}, &block) click to toggle source
# File lib/mobb/base.rb, line 390
def run!(options = {}, &block)
  return if running?

  set options
  handler = detect_repp_handler
  handler_name = handler.name.gsub(/.*::/, '')
  service_settings = settings.respond_to?(:service_settings) ? settings.service_settings : {}
  
  begin
    start_service(handler, service_settings, handler_name, &block)
  rescue => e
    $stderr.puts e.message
    $stderr.puts e.backtrace
  ensure
    quit!
  end
end
running?() click to toggle source
# File lib/mobb/base.rb, line 415
def running?
  running_service?
end
set(option, value = (not_set = true), ignore_setter = false, &block) click to toggle source
# File lib/mobb/base.rb, line 303
def set(option, value = (not_set = true), ignore_setter = false, &block)
  raise ArgumentError if block && !not_set
  value, not_set = block, false if block

  if not_set
    raise ArgumentError unless option.respond_to?(:each)
    option.each { |k,v| set(k,v) }
    return self
  end

  setter_name = "#{option}="
  if respond_to?(setter_name) && ! ignore_setter
    return __send__(setter_name, value)
  end

  setter = proc { |val| set(option, val, true) }
  getter = proc { value }

  case value
  when Proc
    getter = value
  when Symbol, Integer, FalseClass, TrueClass, NilClass
    getter = value.inspect
  when Hash
    setter = proc do |val|
      val = value.merge(val) if Hash === val
      set(option, val, true)
    end
  end

  define_singleton(setter_name, setter)
  define_singleton(option, getter)
  define_singleton("#{option}?", "!!#{option}") unless method_defined?("#{option}?")
  self
end
settings() click to toggle source
# File lib/mobb/base.rb, line 206
def settings
  self
end
silent(cond) click to toggle source
# File lib/mobb/base.rb, line 374
def silent(cond)
  dest_condition do |res|
    res[0] = nil if cond
  end
end
source_condition(name = "
Alias for: condition
test?() click to toggle source
# File lib/mobb/base.rb, line 301
def test?; environment == :test; end
trigger(name, options = {}, &block) click to toggle source
# File lib/mobb/base.rb, line 228
def trigger(name, options = {}, &block) event(:trigger, name, options, &block); end

Private Class Methods

caller_files() click to toggle source
# File lib/mobb/base.rb, line 466
def caller_files
  cleaned_caller(1).flatten
end
cleaned_caller(keep = 3) click to toggle source
# File lib/mobb/base.rb, line 470
def cleaned_caller(keep = 3)
  caller(1).
    map!    { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
    reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
end
define_singleton(name, content = Proc.new) click to toggle source
# File lib/mobb/base.rb, line 459
def define_singleton(name, content = Proc.new)
  singleton_class.class_eval do
    undef_method(name) if method_defined?(name)
    String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
  end
end
detect_repp_handler() click to toggle source
# File lib/mobb/base.rb, line 448
def detect_repp_handler
  services = Array(service)
  services.each do |service_name|
    begin
      return Repp::Handler.get(service_name.to_s)
    rescue LoadError, NameError
    end
  end
  fail "Service handler (#{services.join(',')}) not found"
end
inherited(subclass) click to toggle source
Calls superclass method
# File lib/mobb/base.rb, line 476
def inherited(subclass)
  subclass.reset!
  subclass.set :app_file, caller_files.first unless subclass.app_file?
  super
end
setup_traps() click to toggle source
# File lib/mobb/base.rb, line 433
def setup_traps
  if traps?
    at_exit { quit! }

    [:INT, :TERM].each do |signal|
      old_handler = Signal.trap(signal) do
        quit!
        old_handler.respond_to?(:call) ?  old_handler.call : exit
      end
    end

    disable :traps
  end
end
start_service(handler, service_settings, handler_name) { |service| ... } click to toggle source
# File lib/mobb/base.rb, line 421
def start_service(handler, service_settings, handler_name)
  handler.run(self, service_settings) do |service|
    $stderr.puts "== Mobb (v#{Mobb::VERSION}) is in da house with #{handler_name}. Make some noise!"

    setup_traps
    set running_service: service
    set handler_name: handler_name

    yield service if block_given?
  end
end

Public Instance Methods

call(env) click to toggle source
# File lib/mobb/base.rb, line 62
def call(env)
  dup.call!(env)
end
call!(env) click to toggle source
# File lib/mobb/base.rb, line 66
def call!(env)
  @env = env
  @body = nil
  @repp_options = {}
  @attachments = {}
  @matched = nil
  @silent = false
  invoke { dispatch! }

  @body = nil if @silent
  [@body, @repp_options, @attachments]
end
chain(*args) click to toggle source
# File lib/mobb/base.rb, line 155
def chain(*args)
  payload = args.last.instance_of?(Hash) ? args.pop : nil
  @repp_options[:trigger] = { names: args, payload: payload }
end
dispatch!() click to toggle source
# File lib/mobb/base.rb, line 79
def dispatch!
  # TODO: encode input messages

  invoke do
    filter! :before
    handle_event
  end
ensure
  begin
    filter! :after
  rescue ::Exception => boom
    # TODO: invoke { handle_exception!(boom) }
  end
end
event_eval() { || ... } click to toggle source
# File lib/mobb/base.rb, line 153
def event_eval; throw :halt, yield; end
filter!(type, base = settings) click to toggle source
# File lib/mobb/base.rb, line 109
def filter!(type, base = settings)
  filter! type, base.superclass if base.superclass.respond_to?(:filters)
  base.filters[type].each { |signature|
    # TODO: Refactor compile! and process_event to change conditions in a hash (e,g, { source_cond: [], dest_cond: [] })
    pattern = signature.first
    source_conditions = signature[2]
    wrapper = signature[1]
    process_event(pattern, source_conditions, wrapper)
  }
end
handle_event(base = settings, passed_block = nil) click to toggle source
# File lib/mobb/base.rb, line 120
def handle_event(base = settings, passed_block = nil)
  if responds = base.events[@env.event_type]
    responds.each do |pattern, block, source_conditions, dest_conditions|
      process_event(pattern, source_conditions) do |*args|
        event_eval do
          res = block[*args]
          dest_conditions.inject(res) { |acc, c| c.bind(self).call(acc) }
        end
      end
    end
  end

  # TODO: Define respond missing if receive reply message
  nil
end
invoke() { || ... } click to toggle source
# File lib/mobb/base.rb, line 94
def invoke
  res = catch(:halt) { yield }
  return if res.nil?
  
  res = [res] if String === res
  if Array === res && String === res.first
    tmp = res.dup
    @body = tmp.shift
    @attachments = tmp.pop
  else
    @attachments = res
  end
  nil
end
pass(&block) click to toggle source
# File lib/mobb/base.rb, line 162
def pass(&block)
  throw :pass, block
end
process_event(pattern, conditions, block = nil, values = []) { |self, captures| ... } click to toggle source
# File lib/mobb/base.rb, line 136
def process_event(pattern, conditions, block = nil, values = [])
  res = pattern.match?(@env.body)
  catch(:pass) do
    conditions.each { |c| throw :pass unless c.bind(self).call }

    case res
    when ::Mobb::Matcher::Matched
      @matched = res.matched
      block ? block[self, res.captures] : yield(self, res.captures)
    when TrueClass
      block ? block[self] : yield(self)
    else
      nil
    end
  end
end
say_nothing() click to toggle source
# File lib/mobb/base.rb, line 160
def say_nothing; @silent = true; end
settings() click to toggle source
# File lib/mobb/base.rb, line 166
def settings
  self.class.settings
end