start_her 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 89af5e5e0b4dece5596bbb0acd862628bb473bf5
4
+ data.tar.gz: cb1717a5d3d44ec220c86a128eccb1a082d6b2d0
5
+ SHA512:
6
+ metadata.gz: 812cd8c890e422de9c379118717c8f17e6fb9258a28c26d4b7edcafc2fcc6b96e042c25baad0a7e331ffe14a4071c137fb590945b7ebde3656376ade30a82466
7
+ data.tar.gz: e410c7c43dea121c02e1456779480c8a9224d694b5f1b6067568f861b009c838a5561997305ebccee9b72149dc3a3c77c76ef79d1c6dad03cf346f9f18f08979
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+ /db/*.sqlite3-journal
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*.log
16
+ /tmp
17
+ /public
18
+ /storage
19
+ /pkg
data/.rubocop.yml ADDED
@@ -0,0 +1,485 @@
1
+ # https://github.com/bbatsov/rubocop/tree/master/config
2
+ # => default.yml
3
+ # => disabled.yml
4
+ # => enabled.yml
5
+
6
+ # This is the default configuration file. Enabling and disabling is configured
7
+ # in separate files. This file adds all other parameters apart from Enabled.
8
+
9
+ # inherit_from:
10
+ # - enabled.yml
11
+ # - disabled.yml
12
+
13
+ # Common configuration.
14
+ AllCops:
15
+ # Include gemspec and Rakefile
16
+ Include:
17
+ - '**/*.gemspec'
18
+ - '**/Rakefile'
19
+ Exclude:
20
+ - 'db/**/*'
21
+ - 'bin/**/*'
22
+ - 'vendor/**/*'
23
+ # By default, the rails cops are not run. Override in project or home
24
+ # directory .rubocop.yml files, or by giving the -R/--rails option.
25
+ RunRailsCops: true
26
+
27
+ # Indent private/protected/public as deep as method definitions
28
+ AccessModifierIndentation:
29
+ EnforcedStyle: indent
30
+ SupportedStyles:
31
+ - outdent
32
+ - indent
33
+
34
+ # Align the elements of a hash literal if they span more than one line.
35
+ AlignHash:
36
+ # Alignment of entries using hash rocket as separator. Valid values are:
37
+ #
38
+ # key - left alignment of keys
39
+ # 'a' => 2
40
+ # 'bb' => 3
41
+ # separator - alignment of hash rockets, keys are right aligned
42
+ # 'a' => 2
43
+ # 'bb' => 3
44
+ # table - left alignment of keys, hash rockets, and values
45
+ # 'a' => 2
46
+ # 'bb' => 3
47
+ EnforcedHashRocketStyle: key
48
+ # Alignment of entries using colon as separator. Valid values are:
49
+ #
50
+ # key - left alignment of keys
51
+ # a: 0
52
+ # bb: 1
53
+ # separator - alignment of colons, keys are right aligned
54
+ # a: 0
55
+ # bb: 1
56
+ # table - left alignment of keys and values
57
+ # a: 0
58
+ # bb: 1
59
+ EnforcedColonStyle: key
60
+ # Select whether hashes that are the last argument in a method call should be
61
+ # inspected? Valid values are:
62
+ #
63
+ # always_inspect - Inspect both implicit and explicit hashes.
64
+ # Registers and offence for:
65
+ # function(a: 1,
66
+ # b: 2)
67
+ # Registers an offence for:
68
+ # function({a: 1,
69
+ # b: 2})
70
+ # always_ignore - Ignore both implicit and explicit hashes.
71
+ # Accepts:
72
+ # function(a: 1,
73
+ # b: 2)
74
+ # Accepts:
75
+ # function({a: 1,
76
+ # b: 2})
77
+ # ignore_implicit - Ingore only implicit hashes.
78
+ # Accepts:
79
+ # function(a: 1,
80
+ # b: 2)
81
+ # Registers an offence for:
82
+ # function({a: 1,
83
+ # b: 2})
84
+ # ignore_explicit - Ingore only explicit hashes.
85
+ # Accepts:
86
+ # function({a: 1,
87
+ # b: 2})
88
+ # Registers an offence for:
89
+ # function(a: 1,
90
+ # b: 2)
91
+ EnforcedLastArgumentHashStyle: always_inspect
92
+ SupportedLastArgumentHashStyles:
93
+ - always_inspect
94
+ - always_ignore
95
+ - ignore_implicit
96
+ - ignore_explicit
97
+
98
+ AlignParameters:
99
+ # Alignment of parameters in multi-line method calls.
100
+ #
101
+ # The `with_first_parameter` style aligns the following lines along the same column
102
+ # as the first parameter.
103
+ #
104
+ # method_call(a,
105
+ # b)
106
+ #
107
+ # The `with_fixed_indentation` style alignes the following lines with one
108
+ # level of indenation relative to the start of the line with the method call.
109
+ #
110
+ # method_call(a,
111
+ # b)
112
+ EnforcedStyle: with_first_parameter
113
+ SupportedStyles:
114
+ - with_first_parameter
115
+ - with_fixed_indentation
116
+
117
+ # Allow safe assignment in conditions.
118
+ AssignmentInCondition:
119
+ AllowSafeAssignment: true
120
+
121
+ AmbiguousOperator:
122
+ Enabled: false
123
+
124
+ BlockNesting:
125
+ Max: 3
126
+
127
+ BracesAroundHashParameters:
128
+ EnforcedStyle: no_braces
129
+ SupportedStyles:
130
+ - braces
131
+ - no_braces
132
+
133
+ # Indentation of `when`.
134
+ CaseIndentation:
135
+ IndentWhenRelativeTo: case
136
+ SupportedStyles:
137
+ - case
138
+ - end
139
+ IndentOneStep: false
140
+
141
+ ClassAndModuleChildren:
142
+ # Checks the style of children definitions at classes and modules.
143
+ #
144
+ # Basically there are two different styles:
145
+ #
146
+ # `nested` - have each child on a separat line
147
+ # class Foo
148
+ # class Bar
149
+ # end
150
+ # end
151
+ #
152
+ # `compact` - combine definitions as much as possible
153
+ # class Foo::Bar
154
+ # end
155
+ #
156
+ # The compact style is only forced, for classes / modules with one child.
157
+ EnforcedStyle: nested
158
+ SupportedStyles:
159
+ - nested
160
+ - compact
161
+
162
+ ClassLength:
163
+ CountComments: false # count full line comments?
164
+ Max: 100
165
+
166
+ # Align with the style guide.
167
+ CollectionMethods:
168
+ PreferredMethods:
169
+ collect: 'map'
170
+ collect!: 'map!'
171
+ inject: 'reduce'
172
+ detect: 'find'
173
+ find_all: 'select'
174
+
175
+ # Checks formatting of special comments
176
+ CommentAnnotation:
177
+ Keywords:
178
+ - TODO
179
+ - FIXME
180
+ - OPTIMIZE
181
+ - HACK
182
+ - REVIEW
183
+
184
+ # Avoid complex methods.
185
+ CyclomaticComplexity:
186
+ Max: 6
187
+
188
+ # Multi-line method chaining should be done with leading dots.
189
+ DotPosition:
190
+ EnforcedStyle: leading
191
+ SupportedStyles:
192
+ - leading
193
+ - trailing
194
+
195
+ # Use empty lines between defs.
196
+ EmptyLineBetweenDefs:
197
+ # If true, this parameter means that single line method definitions don't
198
+ # need an empty line between them.
199
+ AllowAdjacentOneLineDefs: false
200
+
201
+ # Align ends correctly.
202
+ EndAlignment:
203
+ # The value `keyword` means that `end` should be aligned with the matching
204
+ # keyword (if, while, etc.).
205
+ # The value `variable` means that in assignments, `end` should be aligned
206
+ # with the start of the variable on the left hand side of `=`. In all other
207
+ # situations, `end` should still be aligned with the keyword.
208
+ AlignWith: keyword
209
+ SupportedStyles:
210
+ - keyword
211
+ - variable
212
+
213
+ FileName:
214
+ Exclude:
215
+ - Rakefile
216
+ - Gemfile
217
+ - Capfile
218
+
219
+ # Checks use of for or each in multiline loops.
220
+ For:
221
+ EnforcedStyle: each
222
+ SupportedStyles:
223
+ - for
224
+ - each
225
+
226
+ # Enforce the method used for string formatting.
227
+ FormatString:
228
+ EnforcedStyle: format
229
+ SupportedStyles:
230
+ - format
231
+ - sprintf
232
+ - percent
233
+
234
+ # Built-in global variables are allowed by default.
235
+ GlobalVars:
236
+ AllowedVariables: []
237
+
238
+ HashSyntax:
239
+ EnforcedStyle: ruby19
240
+ SupportedStyles:
241
+ - ruby19
242
+ - hash_rockets
243
+
244
+ IfUnlessModifier:
245
+ MaxLineLength: 99
246
+
247
+ # Checks the indentation of the first key in a hash literal.
248
+ IndentHash:
249
+ # The value `special_inside_parentheses` means that hash literals with braces
250
+ # that have their opening brace on the same line as a surrounding opening
251
+ # round parenthesis, shall have their first key indented relative to the
252
+ # first position inside the parenthesis.
253
+ # The value `consistent` means that the indentation of the first key shall
254
+ # always be relative to the first position of the line where the opening
255
+ # brace is.
256
+ EnforcedStyle: special_inside_parentheses
257
+ SupportedStyles:
258
+ - special_inside_parentheses
259
+ - consistent
260
+
261
+ LambdaCall:
262
+ EnforcedStyle: call
263
+ SupportedStyles:
264
+ - call
265
+ - braces
266
+
267
+ LineLength:
268
+ Max: 99
269
+
270
+ MethodDefParentheses:
271
+ EnforcedStyle: require_parentheses
272
+ SupportedStyles:
273
+ - require_parentheses
274
+ - require_no_parentheses
275
+
276
+ MethodLength:
277
+ CountComments: false # count full line comments?
278
+ Max: 16
279
+
280
+ MethodName:
281
+ EnforcedStyle: snake_case
282
+ SupportedStyles:
283
+ - snake_case
284
+ - camelCase
285
+
286
+ NumericLiterals:
287
+ MinDigits: 5
288
+
289
+ ParameterLists:
290
+ Max: 5
291
+ CountKeywordArgs: true
292
+
293
+ # Allow safe assignment in conditions.
294
+ ParenthesesAroundCondition:
295
+ AllowSafeAssignment: true
296
+
297
+ PercentLiteralDelimiters:
298
+ PreferredDelimiters:
299
+ '%': ()
300
+ '%i': ()
301
+ '%q': ()
302
+ '%Q': ()
303
+ '%r': '{}'
304
+ '%s': ()
305
+ '%w': ()
306
+ '%W': ()
307
+ '%x': ()
308
+
309
+ PredicateName:
310
+ NamePrefixBlacklist:
311
+ - is_
312
+ - has_
313
+ - have_
314
+
315
+ RaiseArgs:
316
+ EnforcedStyle: exploded
317
+ SupportedStyles:
318
+ - compact # raise Exception.new(msg)
319
+ - exploded # raise Exception, msg
320
+
321
+
322
+ RedundantReturn:
323
+ # When true allows code like `return x, y`.
324
+ AllowMultipleReturnValues: false
325
+
326
+ RedundantSelf:
327
+ Description: "Don't use self where it's not needed."
328
+ Enabled: false
329
+
330
+ RegexpLiteral:
331
+ # The maximum number of (escaped) slashes that a slash-delimited regexp is
332
+ # allowed to have. If there are more slashes, a %r regexp shall be used.
333
+ MaxSlashes: 1
334
+ Enabled: false
335
+
336
+ Semicolon:
337
+ # Allow ; to separate several expressions on the same line.
338
+ AllowAsExpressionSeparator: false
339
+
340
+ SignalException:
341
+ EnforcedStyle: semantic
342
+ SupportedStyles:
343
+ - only_raise
344
+ - only_fail
345
+ - semantic
346
+
347
+
348
+ SingleLineBlockParams:
349
+ Methods:
350
+ - reduce:
351
+ - a
352
+ - e
353
+ - inject:
354
+ - a
355
+ - e
356
+
357
+ SingleLineMethods:
358
+ AllowIfMethodIsEmpty: true
359
+
360
+ StringLiterals:
361
+ EnforcedStyle: single_quotes
362
+ SupportedStyles:
363
+ - single_quotes
364
+ - double_quotes
365
+
366
+ SpaceAroundEqualsInParameterDefault:
367
+ EnforcedStyle: space
368
+ SupportedStyles:
369
+ - space
370
+ - no_space
371
+
372
+ SpaceBeforeBlockBraces:
373
+ EnforcedStyle: space
374
+ SupportedStyles:
375
+ - space
376
+ - no_space
377
+
378
+ SpaceInsideBlockBraces:
379
+ EnforcedStyle: space
380
+ SupportedStyles:
381
+ - space
382
+ - no_space
383
+ # Valid values are: space, no_space
384
+ EnforcedStyleForEmptyBraces: no_space
385
+ # Space between { and |. Overrides EnforcedStyle if there is a conflict.
386
+ SpaceBeforeBlockParameters: true
387
+
388
+ SpaceInsideHashLiteralBraces:
389
+ EnforcedStyle: space
390
+ EnforcedStyleForEmptyBraces: no_space
391
+ SupportedStyles:
392
+ - space
393
+ - no_space
394
+
395
+ TrailingComma:
396
+ EnforcedStyleForMultiline: no_comma
397
+ SupportedStyles:
398
+ - comma
399
+ - no_comma
400
+
401
+ # TrivialAccessors doesn't require exact name matches and doesn't allow
402
+ # predicated methods by default.
403
+ TrivialAccessors:
404
+ ExactNameMatch: false
405
+ AllowPredicates: false
406
+ Whitelist:
407
+ - to_ary
408
+ - to_a
409
+ - to_c
410
+ - to_enum
411
+ - to_h
412
+ - to_hash
413
+ - to_i
414
+ - to_int
415
+ - to_io
416
+ - to_open
417
+ - to_path
418
+ - to_proc
419
+ - to_r
420
+ - to_regexp
421
+ - to_str
422
+ - to_s
423
+ - to_sym
424
+
425
+ VariableName:
426
+ EnforcedStyle: snake_case
427
+ SupportedStyles:
428
+ - snake_case
429
+ - camelCase
430
+
431
+ WhileUntilModifier:
432
+ MaxLineLength: 79
433
+
434
+ WordArray:
435
+ MinSize: 0
436
+
437
+ Documentation:
438
+ Enabled: false
439
+
440
+ # Compact name spaces
441
+ ClassAndModuleChildren:
442
+ Enabled: false
443
+
444
+ AlignParameters:
445
+ Enabled: false
446
+
447
+ # Unrecognize fail/raise args with custom erors
448
+ RaiseArgs:
449
+ EnforcedStyle: compact
450
+
451
+ ##################### Rails ##################################
452
+
453
+ ActionFilter:
454
+ EnforcedStyle: action
455
+ SupportedStyles:
456
+ - action
457
+ - filter
458
+ Include:
459
+ - app/controllers/*.rb
460
+
461
+ DefaultScope:
462
+ Include:
463
+ - app/models/*.rb
464
+
465
+ HasAndBelongsToMany:
466
+ Include:
467
+ - app/models/*.rb
468
+
469
+ ReadWriteAttribute:
470
+ Include:
471
+ - app/models/*.rb
472
+
473
+ ScopeArgs:
474
+ Include:
475
+ - app/models/*.rb
476
+
477
+ Validation:
478
+ Include:
479
+ - app/models/*.rb
480
+
481
+ Encoding:
482
+ Enabled: false
483
+
484
+ HasAndBelongsToMany:
485
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,67 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ start_her (0.0.1)
5
+ gemsupport (~> 0.4)
6
+ logstash-logger (~> 0.15)
7
+ msgpack (~> 0.7)
8
+ redis (~> 3.2)
9
+ redis-namespace (~> 1.5)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ ast (2.1.0)
15
+ astrolabe (1.3.1)
16
+ parser (~> 2.2)
17
+ coderay (1.1.0)
18
+ diff-lcs (1.2.5)
19
+ gemsupport (0.4.1)
20
+ logstash-event (1.2.02)
21
+ logstash-logger (0.15.1)
22
+ logstash-event (~> 1.2)
23
+ stud
24
+ method_source (0.8.2)
25
+ msgpack (0.7.0)
26
+ parser (2.2.3.0)
27
+ ast (>= 1.1, < 3.0)
28
+ powerpack (0.1.1)
29
+ pry (0.10.3)
30
+ coderay (~> 1.1.0)
31
+ method_source (~> 0.8.1)
32
+ slop (~> 3.4)
33
+ rainbow (2.0.0)
34
+ redis (3.2.1)
35
+ redis-namespace (1.5.2)
36
+ redis (~> 3.0, >= 3.0.4)
37
+ rspec (3.3.0)
38
+ rspec-core (~> 3.3.0)
39
+ rspec-expectations (~> 3.3.0)
40
+ rspec-mocks (~> 3.3.0)
41
+ rspec-core (3.3.2)
42
+ rspec-support (~> 3.3.0)
43
+ rspec-expectations (3.3.1)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.3.0)
46
+ rspec-mocks (3.3.2)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.3.0)
49
+ rspec-support (3.3.0)
50
+ rubocop (0.29.1)
51
+ astrolabe (~> 1.3)
52
+ parser (>= 2.2.0.1, < 3.0)
53
+ powerpack (~> 0.1)
54
+ rainbow (>= 1.99.1, < 3.0)
55
+ ruby-progressbar (~> 1.4)
56
+ ruby-progressbar (1.7.5)
57
+ slop (3.6.0)
58
+ stud (0.0.22)
59
+
60
+ PLATFORMS
61
+ ruby
62
+
63
+ DEPENDENCIES
64
+ pry (~> 0.10)
65
+ rspec (~> 3.3)
66
+ rubocop (~> 0.29)
67
+ start_her!
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2015 PredicSis (MIT)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ [![Circle CI](https://circleci.com/gh/PredicSis/start_her/tree/master.svg?style=shield)](https://circleci.com/gh/PredicSis/start_her/tree/master)
2
+
3
+ # StartHer
4
+ Microservices common stuff. Implement redis subscription and logging strategy.
5
+
6
+ ## Requirements
7
+
8
+ - Ruby >= 2.2
9
+ - Redis >= 2.8
10
+
11
+ ## Installation
12
+
13
+ ```ruby
14
+ gem 'start_her'
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ ```ruby
20
+ require 'start_her'
21
+
22
+ StartHer.configure do |config|
23
+ config.redis = {
24
+ url: 'redis://localhost:6379',
25
+ namespace: 'namespace'
26
+ db: 1
27
+ }
28
+ config.logger = StartHer::Logger.instance # default
29
+ config.logstash_url = 'redis://localhost:6379' # if using default logger in production
30
+ end
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```bash
36
+ bundle exec starther ./lib/path/to/subscriber.rb
37
+ ```
38
+
39
+ **Note**: You need to put your `subscriber.rb` file into the `lib` directory.
40
+
41
+ ## Subscriber class
42
+
43
+ ```ruby
44
+ class Subscriber
45
+ include StartHer::Subscriber
46
+
47
+ subscriber_options channels: ['channel_one', 'channel_two']
48
+ subscriber_error do |error|
49
+ # do something with error
50
+ end
51
+
52
+ def process_message(channel, message)
53
+ # your custom stuff here
54
+ end
55
+ end
56
+ ```
57
+
58
+ ## LICENSE
59
+
60
+ MIT
61
+
62
+ ## Contributing
63
+
64
+ 1. Fork it
65
+ 2. Create your feature branch (git checkout -b my-new-feature)
66
+ 3. Commit your changes (git commit -am 'Add some feature')
67
+ 4. Push to the branch (git push origin my-new-feature)
68
+ 5. Create new Pull Request
data/bin/starther ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ # use `bundle install --standalone' to get this...
5
+ require_relative '../bundle/bundler/setup'
6
+ rescue LoadError
7
+ # fall back to regular bundler if the developer hasn't bundled standalone
8
+ require 'bundler'
9
+ Bundler.setup
10
+ end
11
+
12
+ require_relative '../lib/start_her'
13
+
14
+ $stdout.sync = true
15
+ load ARGV.last
16
+ Object.const_get(ARGV.last.split('lib/').last.split('.rb').first.camelize).run!
data/circle.yml ADDED
@@ -0,0 +1,9 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.2
4
+
5
+ test:
6
+ pre:
7
+ - bundle exec rubocop
8
+ override:
9
+ - bundle exec rspec
data/lib/start_her.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'forwardable'
2
+ require 'gemsupport'
3
+ require 'start_her/configuration'
4
+ require 'start_her/logger'
5
+ require 'start_her/retry_policies'
6
+
7
+ module StartHer
8
+ module_function
9
+
10
+ extend SingleForwardable
11
+
12
+ attr_accessor :config
13
+
14
+ def config
15
+ @config ||= Configuration.new
16
+ end
17
+ delegate logger: :config
18
+
19
+ def configure
20
+ yield config
21
+ end
22
+
23
+ def env
24
+ (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development').tap do |e|
25
+ fail 'Invalid environment name' unless %w(development test staging production).include?(e)
26
+ end
27
+ end
28
+
29
+ # Safe loading for stuff that needs MailHer#config
30
+ autoload :RedisClient, 'start_her/redis_client'
31
+ autoload :Subscriber, 'start_her/subscriber'
32
+ end
@@ -0,0 +1,15 @@
1
+ module StartHer
2
+ class Configuration
3
+ attr_accessor :redis, :logstash_url
4
+ attr_writer :logger
5
+
6
+ def initialize
7
+ @redis = {}
8
+ @logstash_url = ''
9
+ end
10
+
11
+ def logger
12
+ @logger ||= StartHer::Logger.instance
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ require 'logstash-logger'
2
+
3
+ module StartHer
4
+ module Logger
5
+ module_function
6
+
7
+ def instance
8
+ send(StartHer.env)
9
+ end
10
+
11
+ def development
12
+ LogStashLogger.new(type: :multi_delegator, outputs: [
13
+ { type: :stdout },
14
+ { type: :file, path: 'log/development.log', sync: true }
15
+ ], formatter: ::Logger::Formatter)
16
+ end
17
+
18
+ def production
19
+ LogStashLogger.new(
20
+ type: :redis, url: StartHer.config.logstash_url, sync: true).tap do |logger|
21
+ logger.level = ::Logger::INFO
22
+ end
23
+ end
24
+ class << self
25
+ alias_method :staging, :production
26
+ end
27
+
28
+ def test
29
+ LogStashLogger.new(type: :stdout, formatter: ::Logger::Formatter)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,35 @@
1
+ require 'redis'
2
+ require 'redis-namespace'
3
+ require 'msgpack'
4
+
5
+ module StartHer
6
+ module RedisClient
7
+ include StartHer::RetryPolicies
8
+
9
+ MQ_DEFAULTS = StartHer.config.redis
10
+
11
+ private
12
+
13
+ def client(options = {})
14
+ options = client_opts(options)
15
+ client = ::Redis.new(options)
16
+
17
+ if (namespace = options[:namespace])
18
+ ::Redis::Namespace.new(namespace, redis: client)
19
+ else
20
+ client
21
+ end
22
+ end
23
+
24
+ def client_opts(options)
25
+ MQ_DEFAULTS.merge(options).dup.tap do |opts|
26
+ if opts[:network_timeout]
27
+ opts[:timeout] = opts[:network_timeout]
28
+ opts.delete(:network_timeout)
29
+ end
30
+
31
+ opts[:driver] = opts[:driver] || 'ruby'
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ module StartHer
2
+ module RetryPolicies
3
+ # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
4
+ def exponential_backoff(opts = {}, *exception_classes)
5
+ options = { elapsed_time_secs: 600, multiplier: 2 }
6
+ options.merge!(opts)
7
+
8
+ time = 0.2
9
+ started_at = Time.now.utc
10
+
11
+ begin
12
+ yield
13
+ rescue *exception_classes => ex
14
+ raise ex if (Time.now.utc - started_at) >= options[:elapsed_time_secs]
15
+ sleep(time)
16
+
17
+ time *= options[:multiplier]
18
+ if (Time.now.utc - started_at) + time >= options[:elapsed_time_secs]
19
+ time = options[:elapsed_time_secs] - (Time.now.utc - started_at)
20
+ end
21
+
22
+ retry
23
+ end
24
+ end
25
+ # rubocop:enable Metrics/AbcSize,Metrics/MethodLength
26
+ end
27
+ end
@@ -0,0 +1,62 @@
1
+ module StartHer
2
+ module Subscriber
3
+ include StartHer::RedisClient
4
+
5
+ DEFAULT_OPTS = { channels: ['*'] }
6
+ DEFAULT_ERROR_BLOCK = lambda do |e|
7
+ StartHer.logger.error e
8
+ end
9
+
10
+ def self.included(base)
11
+ base.extend(ClassMethods)
12
+ end
13
+
14
+ module ClassMethods
15
+ attr_accessor :subscriber_options_hash, :subscriber_error_block
16
+
17
+ def run!
18
+ new.tap do |instance|
19
+ instance.psubscribe(subscriber_options_hash[:channels]) do |channel, message|
20
+ begin
21
+ instance.process_message(channel, message)
22
+ rescue => e
23
+ if block_given?
24
+ subscriber_error_block.call(e)
25
+ else
26
+ DEFAULT_ERROR_BLOCK.call(e)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def subscriber_error(&block)
34
+ self.subscriber_error_block = block
35
+ end
36
+
37
+ def subscriber_options(opts = {})
38
+ self.subscriber_options_hash = DEFAULT_OPTS.merge(opts)
39
+ end
40
+ end
41
+
42
+ # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
43
+ def psubscribe(channels, &block)
44
+ exponential_backoff({}, ::Redis::BaseConnectionError) do
45
+ client.psubscribe(*channels) do |on|
46
+ on.psubscribe do |channel, _|
47
+ StartHer.logger.info "PSuscribe on #{channel}"
48
+ end
49
+
50
+ on.pmessage do |_pattern, channel, message|
51
+ chan = channel.include?(':') ? channel.split(':').last : channel
52
+ block.call(chan, MessagePack.unpack(message))
53
+ end
54
+ end
55
+ end
56
+ rescue => e
57
+ StartHer.logger.error e
58
+ raise e
59
+ end
60
+ # rubocop:enable Metrics/AbcSize,Metrics/MethodLength
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ module StartHer
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,14 @@
1
+ require 'start_her'
2
+
3
+ ENV['RACK_ENV'] ||= 'test'
4
+
5
+ ROOT_SPEC = File.join(File.dirname(__FILE__), '../')
6
+ Dir[File.join(ROOT_SPEC, 'spec', 'support', '**', '*.rb')].each { |f| require f }
7
+
8
+ RSpec.configure do |config|
9
+ config.include Gemsupport::Error
10
+
11
+ config.order = 'random'
12
+ config.run_all_when_everything_filtered = true
13
+ config.filter_run :focus
14
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module StartHer
4
+ describe RedisClient do
5
+ class RedisClientTest
6
+ include RedisClient
7
+ end
8
+
9
+ subject { RedisClientTest.new }
10
+
11
+ describe '#client' do
12
+ context 'with all options' do
13
+ let(:options) { { namespace: 'namespace:', db: 1, url: 'redis://localhost:6379' } }
14
+
15
+ it 'instanciates a Redis::Namespace client' do
16
+ expect(subject.send(:client, options)).to be_an_instance_of Redis::Namespace
17
+ end
18
+ end
19
+
20
+ context 'without namespace' do
21
+ let(:options) { { db: 1, url: 'redis://localhost:6379' } }
22
+
23
+ it 'instanciates a Redis client' do
24
+ expect(subject.send(:client, options)).to be_an_instance_of Redis
25
+ end
26
+ end
27
+
28
+ context 'without any options' do
29
+ let(:options) { {} }
30
+
31
+ it 'instanciates a Redis client' do
32
+ expect(subject.send(:client, options)).to be_an_instance_of Redis
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ module StartHer
4
+ describe RetryPolicies do
5
+ describe '#exponential_backoff' do
6
+ let(:dummy_class) { Class.new.extend(RetryPolicies) }
7
+ let(:options) { { elapsed_time_secs: 1, multiplier: 2 } }
8
+
9
+ it 'updates time with defined policy' do
10
+ retries_time = []
11
+ started_at = Time.now.utc
12
+ CustomHelpers.suppress(RuntimeError) do
13
+ dummy_class.exponential_backoff(options, RuntimeError) do
14
+ retries_time << CustomHelpers.truncate_float(Time.now.utc - started_at, 1)
15
+ fail RuntimeError.new('The error')
16
+ end
17
+ end
18
+ expect(retries_time).to contain_exactly(0.0, 0.2, 0.6, 1.0)
19
+ end
20
+
21
+ context 'when retry policy ends' do
22
+ let(:options) { { elapsed_time_secs: 0.4, multiplier: 2 } }
23
+ let(:exponential_backoff) do
24
+ lambda do |options|
25
+ dummy_class.exponential_backoff(options, RuntimeError) do
26
+ fail RuntimeError.new('The tested error')
27
+ end
28
+ end
29
+ end
30
+
31
+ it 'stops at elapsed time' do
32
+ started_at = Time.now.utc
33
+ CustomHelpers.suppress(RuntimeError) do
34
+ exponential_backoff.call(options)
35
+ end
36
+ expect((Time.now.utc - started_at).round(2)).to eq(options[:elapsed_time_secs])
37
+ end
38
+
39
+ it 'raises an error' do
40
+ expect { exponential_backoff.call(options) }.to raise_error(RuntimeError)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module StartHer
4
+ describe Subscriber do
5
+ let(:channels) { ['channel'] }
6
+
7
+ class TestSubscriber
8
+ include Subscriber
9
+
10
+ subscriber_options channels: ['channel']
11
+
12
+ def process_message(_channel, _message)
13
+ end
14
+ end
15
+
16
+ describe '.run!' do
17
+ it 'subscribes to channels' do
18
+ expect_any_instance_of(Subscriber).to receive(:psubscribe).with(channels)
19
+ TestSubscriber.run!
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe StartHer do
4
+ describe '.env' do
5
+ it 'returns the current environment' do
6
+ expect(StartHer.env).to eq('test')
7
+ end
8
+
9
+ context 'when env is invalid' do
10
+ before { ENV['RACK_ENV'] = 'trololo' }
11
+ after { ENV['RACK_ENV'] = 'test' }
12
+
13
+ it 'raises an error' do
14
+ expect { StartHer.env }.to raise_error(RuntimeError, 'Invalid environment name')
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '.config' do
20
+ it 'returns an option Hash' do
21
+ expect(StartHer.config).to be_an_instance_of(StartHer::Configuration)
22
+ end
23
+ end
24
+
25
+ describe '.logger' do
26
+ it 'returns an instance of logger' do
27
+ expect(StartHer.logger).to be_a(LogStashLogger)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ module CustomHelpers
2
+ include Gemsupport::Error
3
+
4
+ module_function
5
+
6
+ # truncate_float(0.987654, 2) => 0.97
7
+ def truncate_float(value, precision)
8
+ (value * 10**precision).floor / (10**precision).to_f
9
+ end
10
+ end
data/start_her.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'start_her/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'start_her'
9
+ spec.version = StartHer::VERSION
10
+ spec.authors = %w(abourquin mdouchement)
11
+ spec.email = %w(armand.bourquin@predicsis.com marc.douchement@predicsis.com)
12
+ spec.summary = 'StartHer'
13
+ spec.description = 'Micro-service starter'
14
+ spec.homepage = 'https://github.com/PredicSis/start_her'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = ['starther']
19
+ spec.test_files = spec.files.grep(%(r{^(test|spec|features)/}))
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.required_ruby_version = '>= 2.0'
23
+
24
+ # Redis
25
+ spec.add_dependency 'redis', '~> 3.2'
26
+ spec.add_dependency 'redis-namespace', '~> 1.5'
27
+ spec.add_dependency 'msgpack', '~> 0.7'
28
+
29
+ # Logs
30
+ spec.add_dependency 'logstash-logger', '~> 0.15'
31
+
32
+ # Helper
33
+ spec.add_dependency 'gemsupport', '~> 0.4'
34
+
35
+ # Rspec
36
+ spec.add_development_dependency 'rspec', '~> 3.3'
37
+ spec.add_development_dependency 'rubocop', '~> 0.29'
38
+ spec.add_development_dependency 'pry', '~> 0.10'
39
+ end
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: start_her
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - abourquin
8
+ - mdouchement
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-11-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: redis
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '3.2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '3.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: redis-namespace
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.5'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.5'
42
+ - !ruby/object:Gem::Dependency
43
+ name: msgpack
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0.7'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0.7'
56
+ - !ruby/object:Gem::Dependency
57
+ name: logstash-logger
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.15'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.15'
70
+ - !ruby/object:Gem::Dependency
71
+ name: gemsupport
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0.4'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0.4'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rspec
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '3.3'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '3.3'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rubocop
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '0.29'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '0.29'
112
+ - !ruby/object:Gem::Dependency
113
+ name: pry
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '0.10'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '0.10'
126
+ description: Micro-service starter
127
+ email:
128
+ - armand.bourquin@predicsis.com
129
+ - marc.douchement@predicsis.com
130
+ executables:
131
+ - starther
132
+ extensions: []
133
+ extra_rdoc_files: []
134
+ files:
135
+ - ".gitignore"
136
+ - ".rubocop.yml"
137
+ - Gemfile
138
+ - Gemfile.lock
139
+ - LICENSE
140
+ - README.md
141
+ - bin/starther
142
+ - circle.yml
143
+ - lib/start_her.rb
144
+ - lib/start_her/configuration.rb
145
+ - lib/start_her/logger.rb
146
+ - lib/start_her/redis_client.rb
147
+ - lib/start_her/retry_policies.rb
148
+ - lib/start_her/subscriber.rb
149
+ - lib/start_her/version.rb
150
+ - spec/spec_helper.rb
151
+ - spec/start_her/redis_client_spec.rb
152
+ - spec/start_her/retry_policies_spec.rb
153
+ - spec/start_her/subscriber_spec.rb
154
+ - spec/start_her_spec.rb
155
+ - spec/support/custom_helpers.rb
156
+ - start_her.gemspec
157
+ homepage: https://github.com/PredicSis/start_her
158
+ licenses:
159
+ - MIT
160
+ metadata: {}
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '2.0'
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubyforge_project:
177
+ rubygems_version: 2.2.2
178
+ signing_key:
179
+ specification_version: 4
180
+ summary: StartHer
181
+ test_files: []
182
+ has_rdoc: