Class: Mobb::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/mobb/base.rb

Direct Known Subclasses

Application

Constant Summary collapse

CALLERS_TO_IGNORE =
[
  /\/mobb(\/(base|main|show_exceptions))?\.rb$/,   # all sinatra code
  /^\(.*\)$/,                                         # generated code
  /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
  /active_support/,                                   # active_support require hacks
  /bundler(\/runtime)?\.rb/,                          # bundler require hacks
  /<internal:/,                                       # internal in ruby >= 1.9.2
  /src\/kernel\/bootstrap\/[A-Z]/                     # maglev kernel files
]
DEFAULT_CONDITIONS =
{
  :message => {
    :react_to_bot => false,
    :include_myself => false
  }
}

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.eventsObject (readonly)

Returns the value of attribute events.



188
189
190
# File 'lib/mobb/base.rb', line 188

def events
  @events
end

.filtersObject (readonly)

Returns the value of attribute filters.



188
189
190
# File 'lib/mobb/base.rb', line 188

def filters
  @filters
end

Instance Attribute Details

#matchedObject (readonly)

Returns the value of attribute matched.



60
61
62
# File 'lib/mobb/base.rb', line 60

def matched
  @matched
end

Class Method Details

.add_filter(type, pattern = /.*/, **options, &block) ⇒ Object



218
219
220
# 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) ⇒ Object



214
215
216
# File 'lib/mobb/base.rb', line 214

def after(pattern = /.*/, **options, &block)
  add_filter(:after, pattern, options, &block)
end

.before(pattern = /.*/, **options, &block) ⇒ Object



210
211
212
# File 'lib/mobb/base.rb', line 210

def before(pattern = /.*/, **options, &block)
  add_filter(:before, pattern, options, &block)
end

.clear(*options) ⇒ Object



388
# File 'lib/mobb/base.rb', line 388

def clear(*options) options.each { |option| set(option, nil) }; end

.compile(pattern, options) ⇒ Object



268
# File 'lib/mobb/base.rb', line 268

def compile(pattern, options) Matcher.new(pattern, options); end

.compile!(type, pattern, options, &block) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# 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) ⇒ Object



270
271
272
273
274
275
276
# 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 = "#{caller.first[/`.*'/]} condition", &block) ⇒ Object Also known as: source_condition



339
340
341
# File 'lib/mobb/base.rb', line 339

def condition(name = "#{caller.first[/`.*'/]} condition", &block)
  @source_conditions << generate_method(name, &block)
end

.cron(pattern, options = {}, &block) ⇒ Object Also known as: every



225
# File 'lib/mobb/base.rb', line 225

def cron(pattern, options = {}, &block) event(:ticker, pattern, options, &block); end

.dest_condition(name = "#{caller.first[/`.*'/]} condition", &block) ⇒ Object



344
345
346
347
348
349
350
351
352
# 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) ⇒ Object



380
381
382
383
384
# File 'lib/mobb/base.rb', line 380

def dest_to(channel)
  dest_condition do |res|
    res.last[:dest_channel] = channel
  end
end

.development?Boolean

Returns:

  • (Boolean)


299
# File 'lib/mobb/base.rb', line 299

def development?; environment == :development; end

.disable(*options) ⇒ Object



387
# File 'lib/mobb/base.rb', line 387

def disable(*options) options.each { |option| set(option, false) }; end

.enable(*options) ⇒ Object



386
# File 'lib/mobb/base.rb', line 386

def enable(*options) options.each { |option| set(option, true) }; end

.event(type, pattern, options, &block) ⇒ Object



230
231
232
233
234
235
# 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

.extensionsObject



198
199
200
201
202
203
204
# 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) ⇒ Object



278
279
280
281
282
283
# 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) ⇒ Object



285
286
287
288
# 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) ⇒ Object



361
362
363
364
365
366
# 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) ⇒ Object



237
238
239
# 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

.production?Boolean

Returns:

  • (Boolean)


300
# File 'lib/mobb/base.rb', line 300

def production?; environment == :production; end

.quit!Object



408
409
410
411
412
413
# 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) ⇒ Object



354
355
356
357
358
359
# 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) ⇒ Object Also known as: on



222
# File 'lib/mobb/base.rb', line 222

def receive(pattern, options = {}, &block) event(:message, pattern, options, &block); end

.register(*extensions, &block) ⇒ Object



290
291
292
293
294
295
296
297
# 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) ⇒ Object



368
369
370
371
372
# File 'lib/mobb/base.rb', line 368

def reply_to_me(cond)
  condition do
    @env.reply_to.include?(settings.name) == cond
  end
end

.reset!Object



190
191
192
193
194
195
196
# File 'lib/mobb/base.rb', line 190

def reset!
  @events = {}
  @filters = { before: [], after: [] }
  @source_conditions = []
  @dest_conditions = []
  @extensions = []
end

.run!(options = {}, &block) ⇒ Object



390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# 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?Boolean

Returns:

  • (Boolean)


415
416
417
# File 'lib/mobb/base.rb', line 415

def running?
  running_service?
end

.set(option, value = (not_set = true), ignore_setter = false, &block) ⇒ Object

Raises:

  • (ArgumentError)


303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# 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

.settingsObject



206
207
208
# File 'lib/mobb/base.rb', line 206

def settings
  self
end

.silent(cond) ⇒ Object



374
375
376
377
378
# File 'lib/mobb/base.rb', line 374

def silent(cond)
  dest_condition do |res|
    res[0] = nil if cond
  end
end

.test?Boolean

Returns:

  • (Boolean)


301
# File 'lib/mobb/base.rb', line 301

def test?; environment == :test; end

.trigger(name, options = {}, &block) ⇒ Object



228
# File 'lib/mobb/base.rb', line 228

def trigger(name, options = {}, &block) event(:trigger, name, options, &block); end

Instance Method Details

#call(env) ⇒ Object



62
63
64
# File 'lib/mobb/base.rb', line 62

def call(env)
  dup.call!(env)
end

#call!(env) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
# 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) ⇒ Object



155
156
157
158
# 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!Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# 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_evalObject



153
# File 'lib/mobb/base.rb', line 153

def event_eval; throw :halt, yield; end

#filter!(type, base = settings) ⇒ Object



109
110
111
112
113
114
115
116
117
118
# 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) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# 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

#invokeObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# 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) ⇒ Object



162
163
164
# File 'lib/mobb/base.rb', line 162

def pass(&block)
  throw :pass, block
end

#process_event(pattern, conditions, block = nil, values = []) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# 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_nothingObject



160
# File 'lib/mobb/base.rb', line 160

def say_nothing; @silent = true; end

#settingsObject



166
167
168
# File 'lib/mobb/base.rb', line 166

def settings
  self.class.settings
end