bogo 0.2.10 → 0.2.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4147a55ee937c7f32176f7401c9450fae36c3a34
4
- data.tar.gz: fb706011f35266dfd5ad9c84d55d046a0a655db2
2
+ SHA256:
3
+ metadata.gz: 3c54ebb5a6708a864ecac0a28f00ac6195ec7ea8cbf4b45af0938e819dcbb352
4
+ data.tar.gz: d433b7b015699094dfb387e301627259bb1bd5fedce8b737a3975f23a1893903
5
5
  SHA512:
6
- metadata.gz: 31a9b4dad739a3bb2650e9bec4b66c3e064ae09b13ad5b5ee9c3cf147da04acdc7a7c17a327e0b014ea148e4a5ca1b863b9e6bc3f0b00607abd6e697a28fcab0
7
- data.tar.gz: 6a90ab84ac9a68e8f0c78d23d0951564a0277ddd69282ae32604c5299cdb2f0bc2d67bbb6f51242f7ba8ffc67cb936ee400a3d5b5979196371bd88cb21a2304e
6
+ metadata.gz: 976fc3309852ef467a1407a74e70c32b4ebb42d168e28feb32f0a1171239ca26e5dfb8c004ac60daf93cd9aeff4f68ce616728be33cfbe2d485850a5e4888a77
7
+ data.tar.gz: 7bd54f01ae3b64183caf8582e93c2d34e28ddb2e3fbaeeb4eb49b04ef45c2cd3eb0807974749a34fa7dbbbb28d1eecacf71988e9675e51740eecc97ca7282d88
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # v0.2.15
2
+ * [Logger] Fix frozen string modification (#6)
3
+ * [Stack] Add stack
4
+
5
+ # v0.2.14
6
+ * [Logger] Add logging helpers
7
+
8
+ # v0.2.12
9
+ * [Lazy] Add #to_h helper
10
+
1
11
  # v0.2.10
2
12
  * [Lazy] Dup and freeze non-dirty values
3
13
 
data/CONTRIBUTING.md CHANGED
@@ -1,22 +1,18 @@
1
1
  # Contributing
2
2
 
3
- ## Branches
3
+ ## Fixes
4
4
 
5
- ### `master` branch
5
+ Have a fix to some bug you want to submit? Well you're
6
+ awesome. Please just include a description of the bug
7
+ (or link to originating issue) and test coverage on the
8
+ modifications.
6
9
 
7
- The master branch is the current stable released version.
10
+ ## New Features
8
11
 
9
- ### `develop` branch
10
-
11
- The develop branch is the current edge of development.
12
-
13
- ## Pull requests
14
-
15
- * https://github.com/spox/bogo/pulls
16
-
17
- Please base all pull requests of the `develop` branch. Merges to
18
- `master` only occur through the `develop` branch. Pull requests
19
- based on `master` will likely be cherry picked.
12
+ Have a new feature you want to add? Well you're awesome
13
+ too! It may be a good idea to submit an issue first to
14
+ describe the desired feature and get any feedback. Please
15
+ be sure to include tests.
20
16
 
21
17
  ## Issues
22
18
 
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2014 Chris Roberts
1
+ Copyright 2022 Chris Roberts
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
@@ -10,4 +10,4 @@
10
10
  distributed under the License is distributed on an "AS IS" BASIS,
11
11
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  See the License for the specific language governing permissions and
13
- limitations under the License.
13
+ limitations under the License.
data/README.md CHANGED
@@ -376,5 +376,42 @@ between retries at an exponential rate before retry:
376
376
  * `:wait_interval` - Numeric (default: 5)
377
377
  * `:wait_exponent` - Numeric (default: 2)
378
378
 
379
+ ## `Bogo::Logger`
380
+
381
+ This is a wrapped stdlib Logger instance to provide thread-safe
382
+ access for logging. It includes a `Bogo::Logger#named` method for
383
+ creating sub-loggers.
384
+
385
+ ```ruby
386
+ require 'bogo'
387
+
388
+ base = Bogo::Logger.new
389
+ base.progname = 'base'
390
+ base.info 'test'
391
+
392
+ sub = base.named(:sub)
393
+ sub.info 'test'
394
+ ```
395
+
396
+ ### `Bogo::Logger::Helpers`
397
+
398
+ Adds a `#logger` method when included which provides access to the
399
+ global logger. Name can be customized using `.logger_name`.
400
+
401
+ ```ruby
402
+ require 'bogo'
403
+
404
+ class Fubar
405
+ class Thing
406
+ include Bogo::Logger::Helpers
407
+ logger_name(:thing)
408
+
409
+ def test
410
+ logger.info "test"
411
+ end
412
+ end
413
+ end
414
+ ```
415
+
379
416
  # Info
380
- * Repository: https://github.com/spox/bogo
417
+ * Repository: https://github.com/spox/bogo
data/bogo.gemspec CHANGED
@@ -1,19 +1,19 @@
1
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
2
- require 'bogo/version'
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + "/lib/"
2
+ require "bogo/version"
3
3
  Gem::Specification.new do |s|
4
- s.name = 'bogo'
4
+ s.name = "bogo"
5
5
  s.version = Bogo::VERSION.version
6
- s.summary = 'Helper libraries'
7
- s.author = 'Chris Roberts'
8
- s.email = 'code@chrisroberts.org'
9
- s.homepage = 'https://github.com/spox/bogo'
10
- s.description = 'Helper libraries'
11
- s.require_path = 'lib'
12
- s.license = 'Apache 2.0'
13
- s.add_runtime_dependency 'hashie'
14
- s.add_runtime_dependency 'multi_json'
15
- s.add_development_dependency 'pry'
16
- s.add_development_dependency 'minitest'
17
- s.add_development_dependency 'rake', '~> 10'
18
- s.files = Dir['lib/**/*'] + %w(bogo.gemspec README.md CHANGELOG.md CONTRIBUTING.md LICENSE)
6
+ s.summary = "Helper libraries"
7
+ s.author = "Chris Roberts"
8
+ s.email = "code@chrisroberts.org"
9
+ s.homepage = "https://github.com/spox/bogo"
10
+ s.description = "Helper libraries"
11
+ s.require_path = "lib"
12
+ s.license = "Apache 2.0"
13
+ s.add_runtime_dependency "hashie"
14
+ s.add_runtime_dependency "multi_json"
15
+ s.add_development_dependency "pry"
16
+ s.add_development_dependency "minitest"
17
+ s.add_development_dependency "rake"
18
+ s.files = Dir["lib/**/*"] + %w(bogo.gemspec README.md CHANGELOG.md CONTRIBUTING.md LICENSE)
19
19
  end
@@ -1,5 +1,3 @@
1
- require 'bogo'
2
-
3
1
  module Bogo
4
2
  # Animal stylings on strings
5
3
  module AnimalStrings
@@ -24,7 +22,5 @@ module Bogo
24
22
  def snake(string)
25
23
  string.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').gsub('-', '_').downcase
26
24
  end
27
-
28
25
  end
29
-
30
26
  end
@@ -1,10 +1,6 @@
1
- require 'bogo'
2
-
3
1
  module Bogo
4
-
5
2
  # Constant helper
6
3
  module Constants
7
-
8
4
  # Convert string to constant
9
5
  #
10
6
  # @param string [String] full constant name
@@ -40,7 +36,5 @@ module Bogo
40
36
  constantize(klass.join('::'))
41
37
  end
42
38
  end
43
-
44
39
  end
45
-
46
40
  end
@@ -1,15 +1,12 @@
1
- require 'bogo'
2
1
  require 'tempfile'
3
2
 
4
3
  module Bogo
5
4
  # Tempfile that will destroy itself when closed
6
5
  class EphemeralFile < Tempfile
7
-
8
6
  # Override to remove file after close
9
7
  def close
10
8
  super
11
9
  delete
12
10
  end
13
-
14
11
  end
15
12
  end
@@ -7,7 +7,6 @@ require 'http/request'
7
7
  # library to allow easy sharing. It is the responsibility of the user
8
8
  # to ensure the http gem is available!
9
9
  class HTTP::Request
10
-
11
10
  # Override to implicitly apply proxy as required
12
11
  #
13
12
  # NOTE: If dealing with https request, force port so CONNECT request
@@ -54,5 +53,4 @@ class HTTP::Request
54
53
  end
55
54
  "CONNECT #{@uri.host}:#{dest_port} HTTP/#{version}"
56
55
  end
57
-
58
56
  end
data/lib/bogo/lazy.rb CHANGED
@@ -1,21 +1,35 @@
1
- require 'bogo'
2
1
  require 'multi_json'
2
+ require 'monitor'
3
3
  require 'digest/sha2'
4
4
 
5
5
  module Bogo
6
6
  # Adds functionality to facilitate laziness
7
7
  module Lazy
8
-
9
8
  # Instance methods for laziness
10
9
  module InstanceMethods
11
10
 
11
+ def self.included(klass)
12
+ klass.include(MonitorMixin)
13
+ klass.instance_variable_set(:@calling_on_missing, false)
14
+ klass.class_eval do
15
+ alias_method :unlazy_initialize, :initialize
16
+ def initialize(*args, **opts, &block)
17
+ @data = nil
18
+ @dirty = Smash.new
19
+ unlazy_initialize(*args, **opts, &block)
20
+ end
21
+ end
22
+ end
23
+
12
24
  # @return [Smash] argument hash
13
25
  def data
14
- unless(@data)
15
- @data = Smash.new
16
- self.class.attributes.each do |key, value|
17
- if(value.has_key?('default'))
18
- @data[key] = value['default']
26
+ synchronize do
27
+ unless(@data)
28
+ @data = Smash.new
29
+ self.class.attributes.each do |key, value|
30
+ if(value.has_key?('default'))
31
+ @data[key] = value['default']
32
+ end
19
33
  end
20
34
  end
21
35
  end
@@ -24,15 +38,17 @@ module Bogo
24
38
 
25
39
  # @return [Smash] updated data
26
40
  def dirty
27
- unless(@dirty)
28
- @dirty = Smash.new
41
+ synchronize do
42
+ unless(@dirty)
43
+ @dirty = Smash.new
44
+ end
29
45
  end
30
46
  @dirty
31
47
  end
32
48
 
33
49
  # @return [Smash] current data state
34
50
  def attributes
35
- data.merge(dirty)
51
+ synchronize { data.merge(dirty) }
36
52
  end
37
53
 
38
54
  # Create new instance
@@ -40,20 +56,22 @@ module Bogo
40
56
  # @param args [Hash]
41
57
  # @return [self]
42
58
  def load_data(args={})
43
- args = args.to_smash
44
- @data = Smash.new
45
- self.class.attributes.each do |name, options|
46
- val = args[name]
47
- if(options[:required] && !args.has_key?(name) && !options.has_key?(:default))
48
- raise ArgumentError.new("Missing required option: `#{name}`")
49
- end
50
- if(val.nil? && !args.has_key?(name) && options[:default])
51
- if(options[:default])
52
- val = options[:default].respond_to?(:call) ? options[:default].call : options[:default]
59
+ synchronize do
60
+ args = args.to_smash
61
+ @data = Smash.new
62
+ self.class.attributes.each do |name, options|
63
+ val = args[name]
64
+ if(options[:required] && !args.has_key?(name) && !options.has_key?(:default))
65
+ raise ArgumentError.new("Missing required option: `#{name}`")
66
+ end
67
+ if(val.nil? && !args.has_key?(name) && options[:default])
68
+ if(options[:default])
69
+ val = options[:default].respond_to?(:call) ? options[:default].call : options[:default]
70
+ end
71
+ end
72
+ if(args.has_key?(name) || val)
73
+ self.send("#{name}=", val)
53
74
  end
54
- end
55
- if(args.has_key?(name) || val)
56
- self.send("#{name}=", val)
57
75
  end
58
76
  end
59
77
  self
@@ -65,9 +83,11 @@ module Bogo
65
83
  #
66
84
  # @return [self]
67
85
  def valid_state
68
- data.merge!(dirty)
69
- dirty.clear
70
- @_checksum = Digest::SHA256.hexdigest(MultiJson.dump(data.inspect).to_s)
86
+ synchronize do
87
+ data.merge!(dirty)
88
+ dirty.clear
89
+ @_checksum = Digest::SHA256.hexdigest(MultiJson.dump(data.inspect).to_s)
90
+ end
71
91
  self
72
92
  end
73
93
 
@@ -76,14 +96,16 @@ module Bogo
76
96
  # @param attr [String, Symbol] name of attribute
77
97
  # @return [TrueClass, FalseClass] model or attribute is dirty
78
98
  def dirty?(attr=nil)
79
- if(attr)
80
- dirty.has_key?(attr)
81
- else
82
- if(@_checksum)
83
- !dirty.empty? ||
84
- @_checksum != Digest::SHA256.hexdigest(MultiJson.dump(data.inspect).to_s)
99
+ synchronize do
100
+ if(attr)
101
+ dirty.has_key?(attr)
85
102
  else
86
- true
103
+ if(@_checksum)
104
+ !dirty.empty? ||
105
+ @_checksum != Digest::SHA256.hexdigest(MultiJson.dump(data.inspect).to_s)
106
+ else
107
+ true
108
+ end
87
109
  end
88
110
  end
89
111
  end
@@ -98,11 +120,22 @@ module Bogo
98
120
  "<#{self.class.name}:#{object_id} [#{data.inspect}]>"
99
121
  end
100
122
 
123
+ # @return [Hash]
124
+ def to_h
125
+ synchronize do
126
+ Hash[
127
+ attributes.map{|k,v|
128
+ [k, v.is_a?(Array) ?
129
+ v.map{|x| x.respond_to?(:to_h) ? x.to_h : x} :
130
+ v.respond_to?(:to_h) ? v.to_h : v]
131
+ }
132
+ ]
133
+ end
134
+ end
101
135
  end
102
136
 
103
137
  # Class methods for laziness
104
138
  module ClassMethods
105
-
106
139
  # Disable dirty state
107
140
  def always_clean!
108
141
  self.class_eval do
@@ -254,11 +287,9 @@ module Bogo
254
287
  @attributes = attrs.to_smash
255
288
  true
256
289
  end
257
-
258
290
  end
259
291
 
260
292
  class << self
261
-
262
293
  # Injects laziness into class
263
294
  #
264
295
  # @param klass [Class]
@@ -268,16 +299,12 @@ module Bogo
268
299
  extend ClassMethods
269
300
 
270
301
  class << self
271
-
272
302
  def inherited(klass)
273
303
  klass.set_attributes(self.attributes.to_smash)
274
304
  end
275
-
276
305
  end
277
306
  end
278
307
  end
279
-
280
308
  end
281
-
282
309
  end
283
310
  end
@@ -0,0 +1,78 @@
1
+ require "logger"
2
+
3
+ module Bogo
4
+ class Logger
5
+ module Helpers
6
+ module InstanceMethods
7
+ def logger
8
+ self.class.logger
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ def logger_name(name)
14
+ @_logger = logger.named(name.to_s)
15
+ end
16
+
17
+ def logger
18
+ if !@_logger
19
+ base = Utility.snake(self.name.
20
+ split("::").first.downcase)
21
+ @_logger = Bogo::Logger.logger.named(base)
22
+ end
23
+ @_logger
24
+ end
25
+ end
26
+
27
+ def self.included(klass)
28
+ klass.include(InstanceMethods)
29
+ klass.extend(ClassMethods)
30
+ end
31
+ end
32
+
33
+ # @return [Logger] default logger
34
+ def self.logger
35
+ if !@_logger
36
+ @_logger = new
37
+ end
38
+ @_logger
39
+ end
40
+
41
+ # Create a new ::Logger instance wrapped
42
+ # to allow threaded interactions
43
+ def initialize(*logger_args)
44
+ if logger_args.empty?
45
+ logger_args = [$stderr]
46
+ end
47
+ @base_args = logger_args
48
+ @logger = ::Logger.new(*@base_args)
49
+ @logger.level = :fatal
50
+ @lock = Mutex.new
51
+ end
52
+
53
+ # Create a new logger with the sub-name provided
54
+ #
55
+ # @param name [String] sub-name for logger
56
+ # @return [Logger]
57
+ def named(name)
58
+ new_name = self.progname.to_s.dup
59
+ new_name << "." unless new_name.empty?
60
+ new_name << name
61
+ new_logger = Logger.new(*@base_args)
62
+ [:level, :formatter, :datetime_format].each do |m|
63
+ new_logger.send("#{m}=", self.send(m))
64
+ end
65
+ new_logger.progname = new_name
66
+ new_logger
67
+ end
68
+
69
+ ::Logger.instance_methods.each do |l_m|
70
+ next if l_m.to_s.start_with?("_") || l_m.to_s == "object_id"
71
+ class_eval <<-EOC
72
+ def #{l_m}(*ma, &mb)
73
+ @lock.synchronize { @logger.send(:#{l_m}, *ma, &mb) }
74
+ end
75
+ EOC
76
+ end
77
+ end
78
+ end
@@ -1,16 +1,12 @@
1
- require 'bogo'
2
-
3
1
  module Bogo
4
2
  # Memoization helpers
5
3
  module Memoization
6
-
7
4
  # Lock for providing exclusive access
8
5
  EXCLUSIVE_LOCK = Mutex.new
9
6
  # Holder for global memoization items
10
7
  GLOBAL_MEMOS = Smash.new
11
8
 
12
9
  class << self
13
-
14
10
  # Clean up isolated memoizations
15
11
  #
16
12
  # @param object_id [Object]
@@ -38,7 +34,6 @@ module Bogo
38
34
  GLOBAL_MEMOS.clear
39
35
  end
40
36
  end
41
-
42
37
  end
43
38
 
44
39
  # Memoize data
@@ -123,6 +118,5 @@ module Bogo
123
118
  end
124
119
  true
125
120
  end
126
-
127
121
  end
128
122
  end
@@ -1,10 +1,7 @@
1
- require 'bogo'
2
-
3
1
  module Bogo
4
2
  # Specialized priority based queue
5
3
  # @note does not allow duplicate objects to be queued
6
4
  class PriorityQueue
7
-
8
5
  # Create a new priority queue
9
6
  #
10
7
  # @return [self]
@@ -24,10 +21,12 @@ module Bogo
24
21
  def push(item, cost=nil, &block)
25
22
  lock.synchronize do
26
23
  if(queue[item])
27
- raise ArgumentError.new "Item already exists in queue. Items must be unique! (#{item})"
24
+ raise ArgumentError,
25
+ "Item already exists in queue. Items must be unique! (#{item})"
28
26
  end
29
27
  unless(cost || block_given?)
30
- raise ArgumentError.new 'Cost must be provided as parameter or block!'
28
+ raise ArgumentError,
29
+ 'Cost must be provided as parameter or block!'
31
30
  end
32
31
  @block_costs += 1 if cost.nil?
33
32
  queue[item] = cost || block
@@ -45,10 +44,12 @@ module Bogo
45
44
  items.each do |item_pair|
46
45
  item, cost = item_pair
47
46
  if(queue[item])
48
- raise ArgumentError.new "Item already exists in queue. Items must be unique! (#{item})"
47
+ raise ArgumentError,
48
+ "Item already exists in queue. Items must be unique! (#{item})"
49
49
  end
50
50
  unless(cost.is_a?(Numeric) || cost.is_a?(Proc))
51
- raise ArgumentError.new "Cost must be provided as parameter or proc! (item: #{item})"
51
+ raise ArgumentError,
52
+ "Cost must be provided as parameter or proc! (item: #{item})"
52
53
  end
53
54
  @block_costs += 1 if cost.is_a?(Proc)
54
55
  queue[item] = cost
@@ -106,7 +107,5 @@ module Bogo
106
107
  protected
107
108
 
108
109
  attr_reader :queue, :lock
109
-
110
110
  end
111
-
112
111
  end
data/lib/bogo/retry.rb CHANGED
@@ -1,10 +1,6 @@
1
- require 'bogo'
2
-
3
1
  module Bogo
4
-
5
2
  # Perform action and retry until successful or abort
6
3
  class Retry
7
-
8
4
  # Create a type of retry
9
5
  #
10
6
  # @param type [String, Symbol] name of retry type
@@ -38,7 +34,8 @@ module Bogo
38
34
  # @return [self]
39
35
  def initialize(args={}, &block)
40
36
  unless(block)
41
- raise ArgumentError.new 'Expecting block but no block was provided!'
37
+ raise ArgumentError,
38
+ 'Expecting block but no block was provided!'
42
39
  end
43
40
  args = args.to_smash
44
41
  @ui = args[:ui]
@@ -58,7 +55,8 @@ module Bogo
58
55
  # @return [Object] result of action
59
56
  def run!
60
57
  if(dead)
61
- raise RuntimeError.new "Action has already reached maximum allowed attempts (#{max_attempts})!"
58
+ raise RuntimeError,
59
+ "Action has already reached maximum allowed attempts (#{max_attempts})!"
62
60
  else
63
61
  begin
64
62
  log_attempt!
@@ -110,7 +108,6 @@ module Bogo
110
108
 
111
109
  # Flat retry implementation
112
110
  class Flat < Retry
113
-
114
111
  # @return [Numeric]
115
112
  attr_reader :wait_interval
116
113
 
@@ -133,12 +130,10 @@ module Bogo
133
130
  def wait_on_failure(*_)
134
131
  wait_interval
135
132
  end
136
-
137
133
  end
138
134
 
139
135
  # Linear retry implementation
140
136
  class Linear < Retry
141
-
142
137
  # @return [Numeric]
143
138
  attr_reader :wait_interval
144
139
 
@@ -161,12 +156,10 @@ module Bogo
161
156
  def wait_on_failure(*_)
162
157
  wait_interval * attempts
163
158
  end
164
-
165
159
  end
166
160
 
167
161
  # Exponential retry implementation
168
162
  class Exponential < Retry
169
-
170
163
  # @return [Numeric]
171
164
  attr_reader :wait_interval
172
165
  # @return [Numeric]
@@ -196,9 +189,6 @@ module Bogo
196
189
  def wait_on_failure(*_)
197
190
  retries == 0 ? wait_interval : (wait_interval + retries) ** wait_exponent
198
191
  end
199
-
200
192
  end
201
-
202
193
  end
203
-
204
194
  end