sevak 0.2.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 255061d49d7c114c2635cf303f0741b632001222
4
+ data.tar.gz: 72ed6a68f4979c751ff3116c091c397c6b3b7e79
5
+ SHA512:
6
+ metadata.gz: 7572555dc028728c8460d1a3304124cfb548d45e171c9325bd080df76263ee186aaf01042eb22aeb7c4b85aa41680b5c0ea85e6d187d40fe0aaf8481a80c732d
7
+ data.tar.gz: 88fd6c8ece0a9c02216d19f561e87bd01b2726c112b78ee3955b125feff70bf565d480733f9b2bceeb785100e331a5ea96ad64081b081bddb8f5b9954d89594c
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ vendor/
10
+ /log/
11
+ /.idea/
@@ -0,0 +1 @@
1
+ 2.4.0
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.4.0
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sevak.gemspec
4
+ gemspec
5
+
6
+ gem 'bunny'
7
+ gem 'pry'
8
+ gem 'guard'
9
+ gem 'guard-minitest'
10
+ gem 'mocha'
11
+ gem 'highline'
12
+ gem 'rake'
@@ -0,0 +1,68 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sevak (0.2.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ amq-protocol (2.2.0)
10
+ bunny (2.7.0)
11
+ amq-protocol (>= 2.2.0)
12
+ coderay (1.1.2)
13
+ ffi (1.9.18)
14
+ formatador (0.2.5)
15
+ guard (2.14.1)
16
+ formatador (>= 0.2.4)
17
+ listen (>= 2.7, < 4.0)
18
+ lumberjack (~> 1.0)
19
+ nenv (~> 0.1)
20
+ notiffany (~> 0.0)
21
+ pry (>= 0.9.12)
22
+ shellany (~> 0.0)
23
+ thor (>= 0.18.1)
24
+ guard-compat (1.2.1)
25
+ guard-minitest (2.4.6)
26
+ guard-compat (~> 1.2)
27
+ minitest (>= 3.0)
28
+ highline (1.7.8)
29
+ listen (3.1.5)
30
+ rb-fsevent (~> 0.9, >= 0.9.4)
31
+ rb-inotify (~> 0.9, >= 0.9.7)
32
+ ruby_dep (~> 1.2)
33
+ lumberjack (1.0.12)
34
+ metaclass (0.0.4)
35
+ method_source (0.8.2)
36
+ minitest (5.10.3)
37
+ mocha (1.3.0)
38
+ metaclass (~> 0.0.1)
39
+ nenv (0.3.0)
40
+ notiffany (0.1.1)
41
+ nenv (~> 0.1)
42
+ shellany (~> 0.0)
43
+ pry (0.11.0)
44
+ coderay (~> 1.1.0)
45
+ method_source (~> 0.8.1)
46
+ rake (12.1.0)
47
+ rb-fsevent (0.10.2)
48
+ rb-inotify (0.9.10)
49
+ ffi (>= 0.5.0, < 2)
50
+ ruby_dep (1.5.0)
51
+ shellany (0.0.1)
52
+ thor (0.20.0)
53
+
54
+ PLATFORMS
55
+ ruby
56
+
57
+ DEPENDENCIES
58
+ bunny
59
+ guard
60
+ guard-minitest
61
+ highline
62
+ mocha
63
+ pry
64
+ rake
65
+ sevak!
66
+
67
+ BUNDLED WITH
68
+ 1.15.4
@@ -0,0 +1,42 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ guard :minitest do
19
+ # with Minitest::Unit
20
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$})
21
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
22
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
23
+
24
+ # with Minitest::Spec
25
+ # watch(%r{^spec/(.*)_spec\.rb$})
26
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
27
+ # watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
28
+
29
+ # Rails 4
30
+ # watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
31
+ # watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
32
+ # watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
33
+ # watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
34
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
35
+ # watch(%r{^test/.+_test\.rb$})
36
+ # watch(%r{^test/test_helper\.rb$}) { 'test' }
37
+
38
+ # Rails < 4
39
+ # watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
40
+ # watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
41
+ # watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
42
+ end
@@ -0,0 +1 @@
1
+ Copyright (c) 2016 Backwater Technologies Pvt Ltd
@@ -0,0 +1,37 @@
1
+ Sevak gem provides makes it easy to send and receive messages from rabbitmq queues. It is buit on top of the bunny gem.
2
+
3
+
4
+ Usage:
5
+
6
+ Install
7
+
8
+ gem install sevak
9
+
10
+ In your code to publish some message to a queue 'sms'.
11
+
12
+
13
+ Sevak::Publisher.publish('sms', message = { name: 'Deepak', msisdn: '9078657543' })
14
+
15
+ If the queue is not present already it will be created automatically.
16
+
17
+
18
+ To receive message from this queue and process the message a create a consumer.
19
+
20
+
21
+ class SmsConsumer < Sevak::Consumer
22
+
23
+ queue_name 'sms'
24
+
25
+ def run(message)
26
+ status = process(message)
27
+ status
28
+ end
29
+
30
+ ..
31
+ end
32
+
33
+ The return status can have three values :ok, :error, :retry.
34
+
35
+ Publishing to the queue
36
+
37
+ Publisher.publish('in.chillr.email', { name: 'Deepak Kumar', message: 'welcome', email: 'deepak@chillr.in' })
@@ -0,0 +1,85 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'sevak'
3
+ require 'highline'
4
+
5
+ # run tests by running command `rake test`
6
+ require 'rake/testtask'
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs << 'test'
10
+ t.verbose = true
11
+ t.test_files = FileList['test/**/*_spec.rb']
12
+ t.warning = false
13
+ end
14
+
15
+ # Load all rake tasks in the tasks folder
16
+
17
+ tasks = FileList["tasks/*.rake"]
18
+
19
+ tasks.each { |task| load(task) }
20
+
21
+ namespace :consumer do
22
+
23
+ desc 'Interactively create a new consumer'
24
+ task :new do
25
+
26
+ cli = HighLine.new
27
+
28
+ consumer_name = cli.ask('Input consumer name(eg. push_alert) :') { |q| q.validate = /\A[a-z]+[a-z_]+[a-z]\z/}
29
+ queue_name = cli.ask('Input queue name(eg. in.chillr.push.default)') { |q| q.validate = /\A[a-z]+[a-z.]+[a-z]\z/}
30
+
31
+ to_class_name = Proc.new do |name|
32
+ name.split('_').map(&:capitalize).join
33
+ end
34
+
35
+ text1 = <<-CODE
36
+
37
+ module ChillrSevak
38
+
39
+ class #{to_class_name.call(consumer_name)}Consumer < Consumer
40
+
41
+ queue_name "#{queue_name}"
42
+
43
+ def run(payload)
44
+ puts "Consumer running"
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ CODE
51
+
52
+ if File.exists?("lib/sevak/consumers/#{consumer_name}_consumer.rb")
53
+ puts "Already exists file: lib/sevak/consumers/#{consumer_name}_consumer.rb"
54
+ ans = cli.ask('Do you want to overwrite it ? (y/n)') { |q| q.validate = /Y|N|y|n/}
55
+ exit(-1) if ans.downcase == 'n'
56
+ end
57
+
58
+ file = File.open("lib/sevak/consumers/#{consumer_name}_consumer.rb", 'w+')
59
+ file.write(text1)
60
+ file.close
61
+
62
+ text2 = <<-CODE
63
+ namespace :sevak do
64
+
65
+ desc "Run the #{consumer_name} worker"
66
+ task :#{consumer_name} do
67
+ consumer = ChillrSevak::#{to_class_name.call(consumer_name)}Consumer.new
68
+ consumer.start
69
+ end
70
+ end
71
+
72
+ CODE
73
+
74
+ if File.exists?("tasks/#{consumer_name}.rake")
75
+ puts "Already exists file: tasks/#{consumer_name}.rake"
76
+ ans = cli.ask('Do you want to overwrite it ? (y/n)') { |q| q.validate = /Y|N|y|n/}
77
+ exit(-1) if ans.downcase == 'n'
78
+ end
79
+
80
+ file = File.open("tasks/#{consumer_name}.rake", 'w+')
81
+ file.write(text2)
82
+ file.close
83
+
84
+ end
85
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'sevak'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sevak/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'sevak'
8
+ spec.version = Sevak::VERSION
9
+ spec.authors = ['Deepak Kumar']
10
+ spec.email = ['deepakkumarnd@gmail.com']
11
+
12
+ spec.summary = %q{Consumers for chillr api}
13
+ spec.description = %q{Consumers for chillr api}
14
+ spec.homepage = ''
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ # spec.add_development_dependency 'bundler', '~> 1.9'
23
+ # spec.add_development_dependency 'rake', '~> 10.0'
24
+ end
@@ -0,0 +1,5 @@
1
+ host: 'localhost'
2
+ port: '5672'
3
+ user: 'guest'
4
+ password: 'guest'
5
+ prefetch_count: 10
@@ -0,0 +1,110 @@
1
+ Bundler.require(:default)
2
+
3
+ require 'yaml'
4
+ require 'json'
5
+ require 'logger'
6
+ require 'fileutils'
7
+ require 'sevak/version'
8
+
9
+ module Sevak
10
+
11
+ # initialize configuration from the config/*.yml file
12
+ class Config
13
+
14
+ # allowed configurations
15
+ CONFIGURATION = %w(host port user password prefetch_count)
16
+
17
+ def initialize
18
+ load_configuration_from_yml
19
+ end
20
+
21
+ def method_missing(name, *args)
22
+ setter = false
23
+
24
+ name = name.to_s
25
+
26
+ if name =~ /=$/
27
+ name = name.to_s.chop
28
+ setter = true
29
+ end
30
+
31
+ super(name, args) unless CONFIGURATION.include?(name)
32
+
33
+ if setter
34
+ set(name, args.first)
35
+ else
36
+ get(name)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def load_configuration_from_yml
43
+ @config = YAML.load(File.read('config/default.yml'))
44
+ end
45
+
46
+ def set(key, val)
47
+ @config[key] = val
48
+ end
49
+
50
+ def get(key)
51
+ @config[key]
52
+ end
53
+ end
54
+
55
+ def self.config
56
+ @config ||= Config.new
57
+ end
58
+
59
+ def self.configure
60
+ yield(config) if block_given?
61
+ config
62
+ end
63
+
64
+ def self.establish_connection
65
+ return @conn if @conn
66
+
67
+ @conn ||= Bunny.new(
68
+ host: config.host,
69
+ port: config.port,
70
+ username: config.user,
71
+ password: config.password)
72
+
73
+ @conn.start
74
+ @conn
75
+ end
76
+
77
+ def self.get_logger
78
+ if !Dir.exist? 'log'
79
+ FileUtils.mkdir('log')
80
+ end
81
+
82
+ logfile = if !testing?
83
+ File.open('log/sevak.log', 'a')
84
+ else
85
+ File.open('log/sevak_test.log', 'a')
86
+ end
87
+
88
+ # TODO make configurable
89
+ logfile.sync = true
90
+ @logger = Logger.new(logfile, 'weekly')
91
+ end
92
+
93
+ def self.log(data)
94
+ @logger ||= get_logger
95
+ @logger.info(data.inspect)
96
+ end
97
+
98
+ def self.testing?
99
+ defined?(SEVAK_ENV) && (SEVAK_ENV == 'test')
100
+ end
101
+
102
+ def self.root
103
+ File.expand_path(File.dirname(File.dirname(__FILE__)))
104
+ end
105
+
106
+ end
107
+
108
+ require 'sevak/core'
109
+ require 'sevak/consumer'
110
+ require 'sevak/publisher'
@@ -0,0 +1,98 @@
1
+ module Sevak
2
+
3
+ # Base class for all queue consumers, all consumers should inherit from the base Sevak::Consumer and must implement a
4
+ # run method. The run method should implement the business logic.
5
+
6
+ class ConsumerBase
7
+
8
+ include Core
9
+
10
+ DEFAULT_PREFETCH_COUNT = 10
11
+
12
+ # class methods
13
+ def self.queue_name(name='default')
14
+ @queue_name ||= name
15
+ end
16
+ # end of class methods
17
+
18
+ def initialize
19
+ @queue_name = self.class.queue_name
20
+ end
21
+
22
+ def queue_name
23
+ @queue_name
24
+ end
25
+
26
+ def queue
27
+ @queue ||= channel.queue(queue_name)
28
+ end
29
+
30
+ def channel
31
+ @channel ||= connection.create_channel
32
+ end
33
+
34
+ def message_count
35
+ queue.message_count
36
+ end
37
+
38
+ def start
39
+ channel.prefetch(config.prefetch_count || DEFAULT_PREFETCH_COUNT)
40
+
41
+ queue.subscribe(manual_ack: true, exclusive: false) do |delivery_info, metadata, payload|
42
+ body = JSON.parse(payload)
43
+
44
+ # p delivery_info
45
+ # p metadata
46
+
47
+ begin
48
+ status = run(body)
49
+ rescue => ex
50
+ Sevak.log(exception_details(ex, payload))
51
+ status = :error
52
+ end
53
+
54
+ if status == :ok
55
+ channel.ack(delivery_info.delivery_tag)
56
+ elsif status == :retry
57
+ channel.reject(delivery_info.delivery_tag, true)
58
+ else # :error, nil etc
59
+ channel.reject(delivery_info.delivery_tag, false)
60
+ end
61
+ end
62
+
63
+ wait_for_threads
64
+ end
65
+
66
+ def wait_for_threads
67
+ sleep
68
+ end
69
+
70
+ def exception_details(e, payload = nil)
71
+ h = {
72
+ source: "#{self.class}",
73
+ type: "#{e.class}",
74
+ message: e.message,
75
+ payload: payload.inspect,
76
+ backtrace: (e.backtrace || []).take(3).join("\n")
77
+ }
78
+
79
+ msg = h.map { |k,v| "#{k.to_s.capitalize}: #{v.to_s}"}.join(' | ')
80
+
81
+ "Sevak Exception: #{msg}"
82
+ end
83
+
84
+ end
85
+
86
+ class Consumer < ConsumerBase
87
+
88
+ # Set the queue name for the consumer
89
+ queue_name 'sevak.default'
90
+
91
+ def run(payload)
92
+ # implement business logic in the corresponding consumer, the run method should respond with
93
+ # status :ok, :error, :retry after the processing is over
94
+ Sevak.log("Implement run method. Payload: #{payload.inspect} #{}")
95
+ :ok
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,11 @@
1
+ module Sevak
2
+ module Core
3
+ def connection
4
+ ::Sevak.establish_connection
5
+ end
6
+
7
+ def config
8
+ ::Sevak.config
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ require 'singleton'
2
+
3
+ module Sevak
4
+ class Publisher
5
+ include Core
6
+ include Singleton
7
+
8
+ def self.publish(queue_name, message)
9
+ instance.channel.queue(queue_name).publish(message.to_json)
10
+ end
11
+
12
+ def channel
13
+ @channel ||= connection.create_channel
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Sevak
2
+ VERSION = '0.2.0'
3
+ end
File without changes
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sevak
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Deepak Kumar
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-11-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Consumers for chillr api
14
+ email:
15
+ - deepakkumarnd@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".ruby-version"
22
+ - ".travis.yml"
23
+ - Gemfile
24
+ - Gemfile.lock
25
+ - Guardfile
26
+ - LICENSE.txt
27
+ - README.md
28
+ - Rakefile
29
+ - bin/console
30
+ - bin/setup
31
+ - chillr_sevak.gemspec
32
+ - config/default.yml.example
33
+ - lib/sevak.rb
34
+ - lib/sevak/consumer.rb
35
+ - lib/sevak/core.rb
36
+ - lib/sevak/publisher.rb
37
+ - lib/sevak/version.rb
38
+ - tasks/.keep
39
+ homepage: ''
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.6.8
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Consumers for chillr api
63
+ test_files: []
64
+ has_rdoc: