mobb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 75d6f6e3f306dcd58cd2bcf5f715d860448c90db93f792e500dbd7b8805ae24b
4
+ data.tar.gz: 5ab8a6f5af4bfb8e9ea8ea7176c61f7cadcc0b6ceb43766fc0ae78b53f31db0b
5
+ SHA512:
6
+ metadata.gz: 4881e20aee0c8f698b7e1486f26b7902eff6d945d3ee84d8909ed6e9e532e236093f85abc62dea09807087d2aeaaab70eb22fbfa24d872d016d87fe2bace3475
7
+ data.tar.gz: '09e34fa992c82bb08f10468fbd8fdd3fab272344c1bf899384727e10a95504dd77be77d19267d8f3d973e576a2f1b2c6641b8df0561f72ffb68ebf937e91a78a'
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 kinoppyd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Mobb
2
+
3
+ Mobb is the simplest, most lightweight, fastest Bot framework written by Ruby.
4
+
5
+ # Install
6
+
7
+ you can install Mobb by rubygems like
8
+
9
+ ```
10
+ gem isntall mobb
11
+ ```
12
+
13
+ or you can use a bundler. Writes the following in Gemfile
14
+
15
+ ```
16
+ source "https://rubygems.org"
17
+
18
+ gem "mobb", "~> 0.1"
19
+ ```
20
+
21
+ and install
22
+
23
+ ```
24
+ bundle install
25
+ ```
26
+
27
+ # Examples
28
+
29
+ Write your logic in `app.rb` like...
30
+
31
+ ```ruby
32
+ require 'mobb'
33
+
34
+ set :name, "example bot"
35
+ '
36
+ on "hello" do
37
+ "hi! i'm #{settings.name}!"
38
+ end
39
+ ```
40
+
41
+ and start mobb application
42
+
43
+ ```
44
+ ruby app.rb
45
+ ```
46
+
47
+ then the shell will start to wait for your input, so you can type 'hello' and hit enter, then you get.
48
+
49
+ ```
50
+ hi! i'm example bot!"
51
+ ```
52
+
53
+ # Service handlers
54
+
55
+ Mobb is implemented based on [Repp](https://github.com/kinoppyd/repp) Interface.
56
+ Shell and Slack adapter is currently available.
57
+
58
+ ```ruby
59
+ require 'mobb'
60
+ set :serice, 'slack'
61
+
62
+ on /hey (\w+)/ do |someone|
63
+ "hey #{someone}, waz up?"
64
+ end
65
+ ```
66
+
67
+ # TODO
68
+
69
+ + Test, Test, Test
70
+ + Documents
71
+ + Parallel event handling
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,23 @@
1
+ require 'mobb'
2
+
3
+ set :service, 'slack'
4
+ set :name, "example bot"
5
+
6
+ on "hello" do
7
+ "Hi! I'm #{settings.name}"
8
+ end
9
+
10
+ # Warning this is bad implementation
11
+ # inifinity loop happend
12
+ receive "Yo", laziness: true do
13
+ "Yo"
14
+ end
15
+
16
+ receive /hey (\w+)/ do |someone|
17
+ "hey #{someone}, waz up?"
18
+ end
19
+
20
+ # This function is not implements yet
21
+ #every 1.day, at: '15:30', exclude: :holiday do
22
+ # 'Stund up daily meeting time!'
23
+ #end
data/lib/mobb.rb ADDED
@@ -0,0 +1 @@
1
+ require "mobb/main"
data/lib/mobb/base.rb ADDED
@@ -0,0 +1,354 @@
1
+ require 'repp'
2
+ require "mobb/version"
3
+
4
+ module Mobb
5
+ class Matcher
6
+ def initialize(pattern, options) @pattern, @options = pattern, options; end
7
+ def regexp?; pattern.is_a?(Regexp); end
8
+ def inspect; "pattern: #{@pattern}, options #{@options}"; end
9
+ def invoke(time = Time.now) @options[:last_invoked] = time; end
10
+
11
+ def match?(context)
12
+ case context
13
+ when String
14
+ string_matcher(context)
15
+ when Time
16
+ # TODO: do something
17
+ false
18
+ when Array
19
+ context.all? { |c| match?(c) }
20
+ else
21
+ false
22
+ end
23
+ end
24
+
25
+ class Matched
26
+ attr_reader :pattern, :matched
27
+ def initialize(pattern, matched) @pattern, @matched = pattern, matched; end
28
+ end
29
+
30
+ def pattern; @pattern; end
31
+
32
+ def string_matcher(string)
33
+ case pattern
34
+ when Regexp
35
+ if res = pattern.match(string)
36
+ Matched.new(pattern, res.captures)
37
+ else
38
+ false
39
+ end
40
+ when String
41
+ @options[:laziness] ? string.include?(pattern) : string == pattern
42
+ else
43
+ false
44
+ end
45
+ end
46
+ end
47
+
48
+ class Base
49
+ def call(env)
50
+ dup.call!(env)
51
+ end
52
+
53
+ def tick(env)
54
+ dup.tick!(env)
55
+ end
56
+
57
+ def call!(env)
58
+ @env = env
59
+ invoke { dispatch! }
60
+ [@body, @attachments]
61
+ end
62
+
63
+ def tick!(env)
64
+ fail # TODO: write logic here
65
+ end
66
+
67
+ def dispatch!
68
+ # TODO: encode input messages
69
+
70
+ invoke do
71
+ # TODO: before filters
72
+ handle_event
73
+ end
74
+ ensure
75
+ # TODO: after fillters
76
+ end
77
+
78
+ def invoke
79
+ res = catch(:halt) { yield }
80
+ return if res.nil?
81
+
82
+ res = [res] if String === res
83
+ if Array === res && String === res.first
84
+ tmp = res.dup
85
+ @body = tmp.shift
86
+ @attachments = tmp.pop
87
+ else
88
+ @attachments = res
89
+ end
90
+ nil
91
+ end
92
+
93
+ def handle_event(base = settings, passed_block = nil)
94
+ if responds = base.events[@env.event_type]
95
+ responds.each do |pattern, block|
96
+ process_event(pattern) do |*args|
97
+ event_eval { block[*args] }
98
+ end
99
+ end
100
+ end
101
+
102
+ # TODO: Define respond missing if receive reply message
103
+ nil
104
+ end
105
+
106
+ def process_event(pattern, block = nil, values = [])
107
+ res = pattern.match?(@env.body)
108
+ case res
109
+ when ::Mobb::Matcher::Matched
110
+ yield(self, *(res.matched))
111
+ when TrueClass
112
+ yield self
113
+ else
114
+ nil
115
+ end
116
+ end
117
+
118
+ def event_eval; throw :halt, yield; end
119
+
120
+ def settings
121
+ self.class.settings
122
+ end
123
+
124
+ class << self
125
+ CALLERS_TO_IGNORE = [
126
+ /\/mobb(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
127
+ /^\(.*\)$/, # generated code
128
+ /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
129
+ /active_support/, # active_support require hacks
130
+ /bundler(\/runtime)?\.rb/, # bundler require hacks
131
+ /<internal:/, # internal in ruby >= 1.9.2
132
+ /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
133
+ ]
134
+
135
+ attr_reader :events
136
+
137
+ def reset!
138
+ @events = {}
139
+ end
140
+
141
+ def settings
142
+ self
143
+ end
144
+
145
+ def receive(pattern, options = {}, &block) event(:message, pattern, options, &block); end
146
+ alias :on :receive
147
+
148
+ #def every(pattern, options = {}, &block) event(:cron, pattern, options, &block); end
149
+
150
+ def event(type, pattern, options, &block)
151
+ (@events[type] ||= []) << compile!(type, pattern, options, &block)
152
+ end
153
+
154
+ def compile!(type, pattern, option, &block)
155
+ matcher = compile(pattern, option)
156
+ unbound_method = generate_method("#{type}", &block)
157
+ wrapper = block.arity != 0 ?
158
+ proc { |instance, args| unbound_method.bind(instance).call(*args) } :
159
+ proc { |instance, args| unbound_method.bind(instance).call }
160
+ [matcher, wrapper]
161
+ end
162
+
163
+ def compile(pattern, options) Matcher.new(pattern, options); end
164
+
165
+ def generate_method(name, &block)
166
+ define_method(name, &block)
167
+ method = instance_method(name)
168
+ remove_method(name)
169
+ method
170
+ end
171
+
172
+ def development?; environment == :development; end
173
+ def production?; environment == :production; end
174
+ def test?; environment == :test; end
175
+
176
+ def set(option, value = (not_set = true), ignore_setter = false, &block)
177
+ raise ArgumentError if block && !not_set
178
+ value, not_set = block, false if block
179
+
180
+ if not_set
181
+ raise ArgumentError unless option.respond_to?(:each)
182
+ option.each { |k,v| set(k,v) }
183
+ return self
184
+ end
185
+
186
+ setter_name = "#{option}="
187
+ if respond_to?(setter_name) && ! ignore_setter
188
+ return __send__(setter_name, value)
189
+ end
190
+
191
+ setter = proc { |val| set(option, val, true) }
192
+ getter = proc { value }
193
+
194
+ case value
195
+ when Proc
196
+ getter = value
197
+ when Symbol, Integer, FalseClass, TrueClass, NilClass
198
+ getter = value.inspect
199
+ when Hash
200
+ setter = proc do |val|
201
+ val = value.merge(val) if Hash === val
202
+ set(option, val, true)
203
+ end
204
+ end
205
+
206
+ define_singleton(setter_name, setter)
207
+ define_singleton(option, getter)
208
+ define_singleton("#{option}?", "!!#{option}") unless method_defined?("#{option}?")
209
+ self
210
+ end
211
+
212
+ def enable(*options) options.each { |option| set(option, true) }; end
213
+ def disable(*options) options.each { |option| set(option, false) }; end
214
+ def clear(*options) options.each { |option| set(option, nil) }; end
215
+
216
+ def run!(options = {}, &block)
217
+ return if running?
218
+
219
+ set options
220
+ handler = detect_repp_handler
221
+ handler_name = handler.name.gsub(/.*::/, '')
222
+ service_settings = settings.respond_to?(:service_settings) ? settings.service_settings : {}
223
+
224
+ begin
225
+ start_service(handler, service_settings, handler_name, &block)
226
+ rescue => e
227
+ $stderr.puts e.message
228
+ $stderr.puts e.backtrace
229
+ ensure
230
+ quit!
231
+ end
232
+ end
233
+
234
+ def quit!
235
+ return unless running?
236
+ running_service.respond_to?(:stop!) ? running_service.stop! : running_service.stop
237
+ $stderr.puts "== Great sound Mobb, thank you so much"
238
+ clear :running_service, :handler_name
239
+ end
240
+
241
+ def running?
242
+ running_service?
243
+ end
244
+
245
+ private
246
+
247
+ def start_service(handler, service_settings, handler_name)
248
+ handler.run(self, service_settings) do |service|
249
+ $stderr.puts "== Mobb (v#{Mobb::VERSION}) is in da house with #{handler_name}. Make some noise!"
250
+
251
+ setup_traps
252
+ set running_service: service
253
+ set handler_name: handler_name
254
+
255
+ yield service if block_given?
256
+ end
257
+ end
258
+
259
+ def setup_traps
260
+ if traps?
261
+ at_exit { quit! }
262
+
263
+ [:INT, :TERM].each do |signal|
264
+ old_handler = Signal.trap(signal) do
265
+ quit!
266
+ old_handler.respond_to?(:call) ? old_handler.call : exit
267
+ end
268
+ end
269
+
270
+ disable :traps
271
+ end
272
+ end
273
+
274
+ def detect_repp_handler
275
+ services = Array(service)
276
+ services.each do |service_name|
277
+ begin
278
+ return Repp::Handler.get(service_name.to_s)
279
+ rescue LoadError, NameError
280
+ end
281
+ end
282
+ fail "Service handler (#{services.join(',')}) not found"
283
+ end
284
+
285
+ def define_singleton(name, content = Proc.new)
286
+ singleton_class.class_eval do
287
+ undef_method(name) if method_defined?(name)
288
+ String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
289
+ end
290
+ end
291
+
292
+ def caller_files
293
+ cleaned_caller(1).flatten
294
+ end
295
+
296
+ def cleaned_caller(keep = 3)
297
+ caller(1).
298
+ map! { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
299
+ reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
300
+ end
301
+
302
+ def inherited(subclass)
303
+ subclass.reset!
304
+ subclass.set :app_file, caller_files.first unless subclass.app_file?
305
+ super
306
+ end
307
+ end
308
+
309
+ reset!
310
+
311
+ set :name, 'mobb'
312
+ set :environment, (ENV['APP_ENV'] || ENV['REPP_ENV'] || :development).to_sym
313
+
314
+ disable :run, :quiet
315
+ clear :running_service, :handler_name
316
+ enable :traps
317
+ set :service, %w[shell]
318
+
319
+ clear :app_file
320
+ end
321
+
322
+ class Application < Base
323
+ set :logging, Proc.new { !test? }
324
+ set :run, Proc.new { !test? }
325
+ clear :app_file
326
+
327
+ def self.register(*extensions, &block)
328
+ added_methods = extensions.flat_map(&:public_instance_methods)
329
+ Delegator.delegate(*added_methods)
330
+ super(*extensions, &block)
331
+ end
332
+ end
333
+
334
+ module Delegator #:nodoc:
335
+ def self.delegate(*methods)
336
+ methods.each do |method_name|
337
+ define_method(method_name) do |*args, &block|
338
+ return super(*args, &block) if respond_to? method_name
339
+ Delegator.target.send(method_name, *args, &block)
340
+ end
341
+ private method_name
342
+ end
343
+ end
344
+
345
+ delegate :receive, :on, #:every,
346
+ :set, :enable, :disable, :clear
347
+
348
+ class << self
349
+ attr_accessor :target
350
+ end
351
+
352
+ self.target = Application
353
+ end
354
+ end
data/lib/mobb/main.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'mobb/base'
2
+
3
+ module Mobb
4
+ class Application < Base
5
+ set app_file: caller_files.first || $0
6
+
7
+ set :run, Proc.new { File.expand_path($0) == File.expand_path(app_file) }
8
+ end
9
+
10
+ at_exit { Application.run! if $!.nil? && Application.run? }
11
+ end
12
+
13
+ extend Mobb::Delegator
14
+
15
+ #class Repp::Builder
16
+ # include Mobb::Delegator
17
+ #end
@@ -0,0 +1,3 @@
1
+ module Mobb
2
+ VERSION = "0.1.0"
3
+ end
data/mobb.gemspec ADDED
@@ -0,0 +1,27 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "mobb/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mobb"
8
+ spec.version = Mobb::VERSION
9
+ spec.authors = ["kinoppyd"]
10
+ spec.email = ["WhoIsDissolvedGirl+github@gmail.com"]
11
+
12
+ spec.summary = %q{Mobb is a lightweight Bot framework like Sinatra}
13
+ spec.description = %q{Mobb provides bot DSL}
14
+ spec.homepage = "https://github.com/kinoppyd/mobb"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.require_paths = ["lib"]
21
+
22
+ #spec.add_dependency "repp", "~> 0.1"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.16"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mobb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - kinoppyd
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-06-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description: Mobb provides bot DSL
56
+ email:
57
+ - WhoIsDissolvedGirl+github@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - examples/slack_bot.rb
68
+ - lib/mobb.rb
69
+ - lib/mobb/base.rb
70
+ - lib/mobb/main.rb
71
+ - lib/mobb/version.rb
72
+ - mobb.gemspec
73
+ homepage: https://github.com/kinoppyd/mobb
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.7.6
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Mobb is a lightweight Bot framework like Sinatra
97
+ test_files: []