startback 0.5.0 → 0.5.1

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
2
  SHA256:
3
- metadata.gz: 492399cde5cbfe2861575824a32c7788fc16137902d5a4194b3662e5306a5968
4
- data.tar.gz: 192d23a34d668bb3ce495c0bb3c724ef58c4e7857bf8e0ed63d5948a76b43f49
3
+ metadata.gz: 61d4c08ece5a3e956448ef53ca8bfa1a0e4e3b5e9280eb39e705858b9939b708
4
+ data.tar.gz: 3c481e062a5ab699d8c72ba9625f7528f41b94f075177df284f238d9117cacce
5
5
  SHA512:
6
- metadata.gz: 54ff69ead9f9b90eccbc9700a46229621cdfe31eb7b887207f6c69c0f755735ae80fc6257ff9ca6e3430d9fc75a2e04fd7b9ea4ecf6744557cb79d418e1ac3b4
7
- data.tar.gz: 71de967d2ebee0cd0596b51532d91a1abc1197cd69d1b6db528fcf6bcd00977c484067a83ba266e37e8d807c3c2bba8efc3b67f822b0540eef9fea3c9578fc5b
6
+ metadata.gz: cc079a6ced8d0cbec9a23b552056d06a4ebc139a5afb54cbc4eadb7c416ca390263e3c3c29c47009e53954db8176305a398fb8ceaddb3696a676bf6d414f0a45
7
+ data.tar.gz: 6ed71d2a2b742a0634cb3fca8c92e1e0b9a835bbf55f9ada5a9144f40184d01338065ce587d98035e6409a31424ef2a01818e566f5a1cb56e618962f7ac360df
@@ -52,6 +52,7 @@ module Startback
52
52
 
53
53
  def call(severity, time, progname, msg)
54
54
  if msg[:error] && msg[:error].respond_to?(:message, true)
55
+ msg[:backtrace] = msg[:error].backtrace[0..25] if severity == "FATAL"
55
56
  msg[:error] = msg[:error].message
56
57
  end
57
58
  {
@@ -5,16 +5,29 @@ module Startback
5
5
  #
6
6
  # This class MUST be overriden:
7
7
  #
8
- # * the `load_raw_data` protected method MUST be implemented.
9
- # * the `full_key` protected method MAY be overriden to provide specific caching
10
- # keys, e.g. by using the context.
8
+ # * the `load_entity` protected method MUST be implemented to load data from
9
+ # a primary & context unaware key.
10
+ #
11
+ # * the `primary_key` protected method MAY be implemented to convert candidate
12
+ # keys (received from ultimate callers) to primary keys. The method is also
13
+ # a good place to check and/or log the keys actually used by callers.
14
+ #
15
+ # * the `context_free_key` protected method MAY be overriden to provide
16
+ # domain unrelated caching keys from primary keys, e.g. by encoding the
17
+ # context into the caching key itself, if needed.
18
+ #
11
19
  # * the `valid?` protected method MAY be overriden to check validity of data
12
- # extracted from the cache.
20
+ # extracted from the cache and force a refresh even if found.
13
21
  #
14
22
  # An EntityCache takes an actual store at construction. The object must meet the
15
23
  # specification writtern in Store. The 'cache' ruby gem can be used in practice.
16
24
  #
25
+ # Cache hits, outdated and miss are logged in debug, info, and info severity.
26
+ # The `cache_hit`, `cache_outdated`, `cache_miss` protected methods MAY be
27
+ # overriden to change that behavior.
28
+ #
17
29
  class EntityCache
30
+ include Support::Robustness
18
31
 
19
32
  class << self
20
33
 
@@ -37,42 +50,106 @@ module Startback
37
50
  #
38
51
  # If the entity is not in cache, loads it and puts it in cache using
39
52
  # the caching options passed as second parameter.
40
- def get(short_key, caching_options = default_caching_options)
41
- cache_key = encode_key(full_key(short_key))
53
+ def get(candidate_key, caching_options = default_caching_options)
54
+ pkey = primary_key(candidate_key)
55
+ cache_key = encode_key(context_free_key(pkey))
42
56
  if store.exist?(cache_key)
43
57
  cached = store.get(cache_key)
44
- return cached if valid?(cache_key, cached)
58
+ if valid?(pkey, cached)
59
+ cache_hit(pkey, cached)
60
+ return cached
61
+ else
62
+ cache_outdated(pkey, cached)
63
+ end
45
64
  end
46
- load_raw_data(short_key).tap{|to_cache|
65
+ cache_miss(pkey)
66
+ load_entity(pkey).tap{|to_cache|
47
67
  store.set(cache_key, to_cache, caching_options)
48
68
  }
49
69
  end
50
70
 
51
71
  # Invalidates the cache under a given key.
52
- def invalidate(key)
53
- store.delete(encode_key(full_key(key)))
72
+ def invalidate(candidate_key)
73
+ pkey = primary_key(candidate_key)
74
+ cache_key = encode_key(context_free_key(pkey))
75
+ store.delete(cache_key)
54
76
  end
55
77
 
56
78
  protected
57
79
 
58
- def encode_key(key)
59
- JSON.fast_generate(key)
80
+ def cache_hit(pkey, cached)
81
+ log(:debug, self, "cache_hit", op_data: pkey)
82
+ end
83
+
84
+ def cache_outdated(pkey, cached)
85
+ log(:info, self, "cache_outdated", op_data: pkey)
86
+ end
87
+
88
+ def cache_miss(pkey)
89
+ log(:info, self, "cache_miss", op_data: pkey)
60
90
  end
61
91
 
62
92
  def default_caching_options
63
93
  { ttl: self.class.default_ttl }
64
94
  end
65
95
 
66
- def valid?(cache_key, cached)
96
+ # Converts a candidate key to a primary key, so as to prevent
97
+ # cache duplicates if callers are allowed to request an entity
98
+ # through various keys.
99
+ #
100
+ # The default implementation returns the candidate key and MAY
101
+ # be overriden.
102
+ def primary_key(candidate_key)
103
+ candidate_key
104
+ end
105
+
106
+ # Encodes a context free key to an actual cache key.
107
+ #
108
+ # Default implementation uses JSON.fast_generate but MAY be
109
+ # overriden.
110
+ def encode_key(context_free_key)
111
+ JSON.fast_generate(context_free_key)
112
+ end
113
+
114
+ # Returns whether `cached` entity seems fresh enough to
115
+ # be returned as a cache hit.
116
+ #
117
+ # This method provides a way to check freshness using, e.g.
118
+ # `updated_at` or `etag` kind of entity fields. The default
119
+ # implementation returns true and MAY be overriden.
120
+ def valid?(primary_key, cached)
67
121
  true
68
122
  end
69
123
 
70
- def full_key(key)
71
- key
124
+ # Converts a primary_key to a context_free_key, using the
125
+ # context (instance variable) to encode the context itself
126
+ # into the actual cache key.
127
+ #
128
+ # The default implementation simply returns the primary key
129
+ # and MAY be overriden.
130
+ def context_free_key(primary_key)
131
+ full_key(primary_key)
132
+ end
133
+
134
+ # Deprecated, will be removed in 0.6.0. Use context_free_key
135
+ # instead.
136
+ def full_key(primary_key)
137
+ primary_key
138
+ end
139
+
140
+ # Actually loads the entity using the given primary key, and
141
+ # possibly the cache context.
142
+ #
143
+ # This method MUST be implemented and raises a NotImplementedError
144
+ # by default.
145
+ def load_entity(primary_key)
146
+ load_raw_data(primary_key)
72
147
  end
73
148
 
74
- def load_raw_data(short_key)
75
- raise NotImplementedError, "#{self.class.name}#load_raw_data"
149
+ # Deprecated, will be removed in 0.6.0. Use load_entity
150
+ # instead.
151
+ def load_raw_data(*args, &bl)
152
+ raise NotImplementedError, "#{self.class.name}#load_entity"
76
153
  end
77
154
 
78
155
  end # class EntityCache
@@ -24,12 +24,13 @@ module Startback
24
24
  #
25
25
  # Examples:
26
26
  #
27
- # log(op: "hello", op_data: {foo: 12}) => logged as such on STDOUT
28
- # log("A simple message") => { op: "A simple message" } on STDOUT
29
- # log(Startback, "hello") => { op: "Startback#hello" } on STDOUT
30
- # log(Event.new, "hello") => { op: "Event#hello" } on STDOUT
31
- # log(self, context) => { op: "..." } on context's logger or STDOUT
32
- # log(self, event) => { op: "..." } on event context's logger or STDOUT
27
+ # log(:info, op: "hello", op_data: {foo: 12}) => logged as such on STDOUT
28
+ # log(:info, "A simple message") => { op: "A simple message" } on STDOUT
29
+ # log(:info, Startback, "hello") => { op: "Startback#hello" } on STDOUT
30
+ # log(:info, Event.new, "hello") => { op: "Event#hello" } on STDOUT
31
+ # log(:info, Event.new, "hello", "hello world") => { op: "Event#hello", op_data: { message: "hello world" } } on STDOUT
32
+ # log(:info, self, context) => { op: "..." } on context's logger or STDOUT
33
+ # log(:info, self, event) => { op: "..." } on event context's logger or STDOUT
33
34
  # ...
34
35
  #
35
36
  module Robustness
@@ -41,7 +42,6 @@ module Startback
41
42
  def default_logger
42
43
  @@default_logger ||= ::Logger.new(STDOUT)
43
44
  from = caller.reject{|x| x =~ /lib\/startback/ }.first
44
- @@default_logger.debug("Watch out, using default logger from: #{from}")
45
45
  @@default_logger
46
46
  end
47
47
  module_function :default_logger
@@ -56,7 +56,8 @@ module Startback
56
56
 
57
57
  def parse_args(log_msg, method = nil, context = nil, extra = nil)
58
58
  method, context, extra = nil, method, context unless method.is_a?(String)
59
- context, extra = nil, context if context.is_a?(Hash) && extra.nil?
59
+ context, extra = nil, context if context.is_a?(Hash) || context.is_a?(String) && extra.nil?
60
+ extra = { op_data: { message: extra } } if extra.is_a?(String)
60
61
  logger = logger_for(context) || logger_for(log_msg)
61
62
  log_msg = if log_msg.is_a?(Hash)
62
63
  log_msg.dup
@@ -98,10 +99,9 @@ module Startback
98
99
  took = Benchmark.realtime {
99
100
  result = bl.call
100
101
  }
101
- Tools.debug(args, op_took: took)
102
+ Tools.info(args, op_took: took)
102
103
  result
103
104
  rescue => ex
104
- Tools.error(args, error: ex)
105
105
  raise
106
106
  end
107
107
 
@@ -117,7 +117,7 @@ module Startback
117
117
  }
118
118
  result
119
119
  rescue => ex
120
- Tools.error(args, op_took: took, error: ex)
120
+ Tools.fatal(args, op_took: took, error: ex)
121
121
  nil
122
122
  end
123
123
 
@@ -2,7 +2,7 @@ module Startback
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 5
5
- TINY = 0
5
+ TINY = 1
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
@@ -33,7 +33,6 @@ module Startback
33
33
  self.body FATAL_ERROR
34
34
 
35
35
  self.ensure(true) do |ex|
36
- STDERR.puts(ex.message)
37
36
  context = env[Context::Middleware::RACK_ENV_KEY]
38
37
  begin
39
38
  if context && context.respond_to?(:error_handler, true) && context.error_handler
@@ -16,6 +16,18 @@ module Startback
16
16
 
17
17
  protected
18
18
 
19
+ def primary_key(ckey)
20
+ case ckey
21
+ when Integer then "a key"
22
+ when String then ckey
23
+ else
24
+ raise "Invalid key `#{ckey}`"
25
+ end
26
+ end
27
+
28
+ # We use the deprecated methods below to test
29
+ # backward compatibility with 0.5.0.
30
+
19
31
  def full_key(key)
20
32
  { k: key }
21
33
  end
@@ -77,6 +89,21 @@ module Startback
77
89
 
78
90
  end
79
91
 
92
+ describe "primary_key" do
93
+
94
+ subject{
95
+ cache.get(12)
96
+ }
97
+
98
+ it 'allows using candidate keys' do
99
+ expect(subject).to eql("a value")
100
+ expect(subject).to eql("a value")
101
+ expect(cache.called).to eql(1)
102
+ expect(cache.last_key).to eql("a key")
103
+ end
104
+
105
+ end
106
+
80
107
  describe "invalidate" do
81
108
 
82
109
  it 'strips the key on the store, yielding a cache miss' do
@@ -61,7 +61,7 @@ module Startback
61
61
  seen += 1
62
62
  raise "Test"
63
63
  end
64
- }.to raise_error
64
+ }.to raise_error("Test")
65
65
  expect(seen).to eql(2)
66
66
  expect(the_logger.last_msg.keys).to eql([:op, :op_took, :error])
67
67
  end
@@ -116,6 +116,16 @@ module Startback
116
116
  expect(logger).to be_a(::Logger)
117
117
  end
118
118
 
119
+ it 'works fine with an string, a method name, and a message' do
120
+ expected = {
121
+ op: "AClass#method",
122
+ op_data: { message: "Hello world" }
123
+ }
124
+ log_msg, logger = parse_args("AClass", "method", "Hello world")
125
+ expect(log_msg).to eql(expected)
126
+ expect(logger).to be_a(::Logger)
127
+ end
128
+
119
129
  it 'works fine with a string only' do
120
130
  expected = {
121
131
  op: "a message"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: startback
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-16 00:00:00.000000000 Z
11
+ date: 2019-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake