start_her 0.0.1

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
+ 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: