lines 0.2.0 → 0.9.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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ZGM0YmFkODI1ZDgwOTA1NTQ1Y2JmZjM5ZDE2ZjIxMzAxM2JhNWZhZg==
5
- data.tar.gz: !binary |-
6
- YzY3M2FhMGEyNmExZGEyODZiMDIyMDgzOWQ4YTQ4NThiMzM2NDI3NQ==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- NTI3YzJhY2M5YjMzZDE2NWI3NjdjYmE0MDZjOWU0MDg1MjY0ZTY2NmYzYThh
10
- ZjkxNTQ4ZjZjMzQ0OGRiMTZmNTc5ZDhmYjI3ODIwMDg4MDg0NTFmYWE4OWU3
11
- MmUyYzgxOTU0MjQ0N2NhNDFmNThkZmU3Y2NjOWMyZWUxMWIwMmU=
12
- data.tar.gz: !binary |-
13
- Njc3MjM5ZjJmMmY0MWE4OWM1ZmFlYmUyNzdlMmQ0OWFmZTVmZTllZjc0YWRk
14
- ZDczMjM4YmEwYTMwYTU1OTU5Y2E1OWIwNjAzMGNiYTc1NTdiNGJhNWUzZjE5
15
- ZjhiNDlhYWU1ZGM4MTBmZTQ3YmY4ZmQyNjMxOWU5NDMyZjZiMWU=
2
+ SHA1:
3
+ metadata.gz: 938854f5278c30f9d2cceb17de859d5293f1eba3
4
+ data.tar.gz: 76e588d5c75ea0df0cbd138e8c6dce199b27ea85
5
+ SHA512:
6
+ metadata.gz: a7fcb7a10b27c6900c0bda6365081cefe0b968c5551089f0741490101b4ec88002694f08c145700cabea3433a28e427d08dd3bd54c268d844ebffd0b6ded74a7
7
+ data.tar.gz: 113168e5ba0ece27faaa8eba50babd78487b74804d27e4a02d1893cae3574a6bdf90cad8bab2a18953bedddbe773b0f8f0f3395bb18c2d680ed0d68b56fd4cfe
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  Gemfile.lock
2
+ *.gem
@@ -3,5 +3,5 @@ language: ruby
3
3
  rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
+ - 2.1.0
6
7
  - jruby-19mode
7
- - rbx-19mode
@@ -1,4 +1,20 @@
1
1
 
2
+ 0.9.1 / 2014-11-12
3
+ ==================
4
+
5
+ Bump after broken release process.
6
+
7
+ 0.9.0 / 2014-11-12
8
+ ==================
9
+
10
+ Moved the logging part to a separate library called u-log.
11
+ http://rubygems.org/gems/u-log
12
+
13
+ * REMOVED: All logging parts
14
+ * NEW: max_bytesize directive to limit line length
15
+ * FIX: BasicObject serialization for ruby 2.1+
16
+ * Tons of cleanup
17
+
2
18
  0.2.0 / 2013-07-15
3
19
  ==================
4
20
 
data/Gemfile CHANGED
@@ -1,10 +1,3 @@
1
1
  source 'http://rubygems.org'
2
2
  gemspec
3
- group :development do
4
- gem 'rdoc'
5
- end
6
- group :test do
7
- gem 'benchmark-ips'
8
- gem 'rake'
9
- gem 'rspec'
10
- end
3
+ gem 'json'
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Jonas Pfenniger
1
+ Copyright (c) 2013 zimbatm
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,90 +1,47 @@
1
1
  Lines - structured logs for humans
2
2
  ==================================
3
3
  [![Build
4
- Status](https://travis-ci.org/zimbatm/lines-ruby.png)](https://travis-ci.org/zimbatm/lines-ruby)
4
+ Status](https://travis-ci.org/zimbatm/lines-ruby.svg?branch=master)](https://travis-ci.org/zimbatm/lines-ruby)
5
5
 
6
- An oppinionated logging library that implement the
6
+ A ruby implementation of the
7
7
  [lines](https://github.com/zimbatm/lines) format.
8
8
 
9
- * Log everything in development AND production.
10
- * Logs should be easy to read, grep and parse.
11
- * Logging something should never fail.
12
- * Let the system handle the storage. Write to syslog or STDERR.
13
- * No log levels necessary. Just log whatever you want.
14
-
15
9
  STATUS: WORK IN PROGRESS
16
10
  ========================
17
11
 
18
- Doc is still scarce so it's quite hard to get started. I think reading the
19
- lib/lines.rb should give a good idea of the capabilities.
20
-
21
- Lines.id is a unique ID generator that seems quite handy but I'm not sure if
22
- it should be part of the lib or not.
23
-
24
- It would be nice to expose a method that resolves a context into a hash. It's
25
- useful to share the context with other tools like an error reporter. Btw,
26
- Sentry/Raven is great.
27
-
28
- There is a parser in the lib but no credible direct consumption path.
29
-
30
- Quick intro
31
- -----------
12
+ Example
13
+ -------
32
14
 
33
15
  ```ruby
34
16
  require 'lines'
35
17
 
36
- # Setups the outputs. IO and Syslog are supported.
37
- Lines.use($stdout, Syslog)
18
+ Lines.dump(foo: 3) #=> "foo=3"
38
19
 
39
- # All lines will be prefixed by the global context
40
- Lines.global['at'] = proc{ Time.now }
20
+ Lines.load("foo=3") #=> {"foo"=>3}
21
+ ```
41
22
 
42
- # First example
43
- Lines.log(foo: 'bar') # logs: at=2013-07-14T14:19:28Z foo=bar
23
+ Uses
24
+ ----
44
25
 
45
- # If not a hash, the argument is transformed. A second argument is accepted as
46
- # a hash
47
- Lines.log("Hey", count: 3) # logs: at=2013-07-14T14:19:28Z msg=Hey count=3
26
+ CLI pipes format
48
27
 
49
- # You can also keep a context
50
- class MyClass < ActiveRecord::Base
51
- attr_reader :lines
52
- def initialize
53
- @lines = Lines.context(my_class_id: self.id)
54
- end
28
+ Structued logging
55
29
 
56
- def do_something
57
- lines.log("Something happened")
58
- # logs: at=2013-07-14T14:19:28Z msg='Something happeend' my_class_id: 2324
59
- end
60
- end
61
- ```
62
30
 
63
- Features
64
- --------
31
+ Generator TODO
32
+ --------------
65
33
 
66
- * Simple to use
67
- * Thread safe (if the IO#write is)
68
- * Designed to not raise exceptions (unless it's an IO issue)
69
- * Lines.logger is a backward-compatible Logger in case you want to retrofit
70
- * require "lines/active_record" for sane ActiveRecord logs
71
- * "lines/rack_logger" is a logging middleware for Rack
72
- * Lines.load and Lines.dump to parse and generate 'lines'
34
+ Add a max_length option
73
35
 
74
- There's also a fork of lograge that you can use with Rails. See
75
- https://github.com/zimbatm/lograge/tree/lines-output
36
+ Make sure the output is encoded as a UTF-8 string
76
37
 
77
- Known issues
78
- ------------
38
+ Parser TODO
39
+ -----------
79
40
 
80
- Syslog seems to truncate lines longer than 2056 chars and Lines makes if very
81
- easy to put too much data.
41
+ Implement the max_nesting option
82
42
 
83
- Lines logging speed is reasonable but it could be faster. It writes at around
84
- 5000 lines per second to Syslog on my machine.
43
+ Different parsing modes. Strict and non-strict. Type templates.
44
+
45
+ Multi-line parsing.
85
46
 
86
- Inspired by
87
- -----------
88
47
 
89
- * Scrolls : https://github.com/asenchi/scrolls
90
- * Lograge : https://github.com/roidrage/lograge
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # Why use -- prefixes in command-line programs ?
3
+ #
4
+ # Here's how we can use lines for a nicer experience:
5
+ #
6
+ # ./cli.rb foo=333 bar=baz 'xxx=[3 4 #t]'
7
+ #
8
+ # Actually zsh makes it less friendly because [] and {} are interpreted
9
+ #
10
+ # FIXME: cli.rb foo='a b'
11
+ # FIXME: cli.rb --foo=abc
12
+
13
+ $:.unshift File.expand_path('../../lib', __FILE__)
14
+ require 'lines'
15
+ p ARGV
16
+ args = Lines.load(ARGV.join(' '))
17
+ p args
@@ -1,413 +1,129 @@
1
- require 'date'
2
- require 'time'
3
-
4
- # Lines is an opinionated structured log format and a library.
5
- #
6
- # Log everything in development AND production.
7
- # Logs should be easy to read, grep and parse.
8
- # Logging something should never fail.
9
- # Let the system handle the storage. Write to syslog or STDERR.
10
- # No log levels necessary. Just log whatever you want.
11
- #
12
- # Example:
13
- #
14
- # log("Oops !", foo: {}, g: [])
15
- # #outputs:
16
- # # at=2013-03-07T09:21:39+00:00 pid=3242 app=some-process msg="Oops !" foo={} g=[]
17
- #
18
- # Usage:
19
- #
20
- # Lines.use(Syslog, $stderr)
21
- # Lines.log(foo: 3, msg: "This")
22
- #
23
- # ctx = Lines.context(encoding_id: Log.id)
24
- # ctx.log({})
25
- #
26
- # Lines.context(foo: 'bar') do |l|
27
- # l.log(items_count: 3)
28
- # end
29
- module Lines
30
- class << self
31
- attr_reader :global
32
- attr_writer :loader, :dumper
33
-
34
- # Parsing object. Responds to #load(string)
35
- def loader
36
- @loader ||= (
37
- require 'lines/loader'
38
- Loader
39
- )
40
- end
41
-
42
- # Serializing object. Responds to #dump(hash)
43
- def dumper; @dumper ||= Dumper.new end
44
-
45
- # Returns a backward-compatibile Logger
46
- def logger
47
- @logger ||= (
48
- require 'lines/logger'
49
- Logger.new(self)
50
- )
51
- end
52
-
53
- # Used to configure lines.
54
- #
55
- # outputs - allows any kind of IO or Syslog
56
- #
57
- # Usage:
58
- #
59
- # Lines.use(Syslog, $stderr, at: proc{ Time.now })
60
- def use(*outputs)
61
- if outputs.last.kind_of?(Hash)
62
- @global = outputs.pop
63
- else
64
- @global = {}
65
- end
66
- @outputters = outputs.flatten.map{|o| to_outputter o}
67
- end
68
-
69
- # The main function. Used to record objects in the logs as lines.
70
- #
71
- # obj - a ruby hash. coerced to +{"msg"=>obj}+ otherwise
72
- # args - complementary values to put in the line
73
- def log(obj, args={})
74
- obj = prepare_obj(obj, args)
75
- @outputters.each{|out| out.output(dumper, obj) }
76
- nil
77
- end
78
-
79
- # Add data to the logs
80
- #
81
- # data - a ruby hash
82
- #
83
- # return a Context instance
84
- def context(data={})
85
- new_context = Context.new ensure_hash!(data)
86
- yield new_context if block_given?
87
- new_context
88
- end
89
-
90
- def ensure_hash!(obj) # :nodoc:
91
- return {} unless obj
92
- return obj if obj.kind_of?(Hash)
93
- return obj.to_h if obj.respond_to?(:to_h)
94
- {msg: obj}
95
- end
96
-
97
- # Parses a lines-formatted string
98
- def load(string)
99
- loader.load(string)
100
- end
101
-
102
- # Generates a lines-formatted string from the given object
103
- def dump(obj)
104
- dumper.dump ensure_hash!(obj)
105
- end
106
-
107
- protected
108
-
109
- def prepare_obj(obj, args={})
110
- if obj.kind_of?(Exception)
111
- ex = obj
112
- obj = {ex: ex.class, msg: ex.to_s}
113
- if ex.respond_to?(:backtrace) && ex.backtrace
114
- obj[:backtrace] = ex.backtrace
115
- end
116
- else
117
- obj = ensure_hash!(obj)
118
- end
119
-
120
- args = ensure_hash!(args)
121
-
122
- g = global.inject({}) do |h, (k,v)|
123
- h[k] = (v.respond_to?(:call) ? v.call : v) rescue $!
124
- h
125
- end
126
-
127
- g.merge(obj.merge(args))
128
- end
129
-
130
- def to_outputter(out)
131
- return out if out.respond_to?(:output)
132
- return StreamOutputter.new(out) if out.respond_to?(:write)
133
- return SyslogOutputter.new if out == ::Syslog
134
- raise ArgumentError, "unknown outputter #{out.inspect}"
135
- end
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'lines/parser'
4
+ require 'lines/generator'
5
+ require 'lines/version'
6
+
7
+ # Lines is an opinionated structured log format
8
+ module Lines; extend self
9
+ # The global default options for the Lines.parse and Lines.load method:
10
+ # symbolize_names: false
11
+ attr_reader :parse_default_options
12
+ @parse_default_options = {
13
+ symbolize_names: false,
14
+ }
15
+
16
+ # The global default options for the Lines.dump method:
17
+ # max_nesting: 4
18
+ # max_size: 2048,
19
+ attr_reader :generate_default_options
20
+ @generate_default_options = {
21
+ max_nesting: 4,
22
+ max_bytesize: 2048,
23
+ }
24
+
25
+ attr_accessor :parser
26
+ @parser = Parser
27
+
28
+ attr_accessor :generator
29
+ @generator = Generator
30
+
31
+ # Parse the Lines string _source_ into a Ruby data structure and return it.
32
+ #
33
+ # _options_ can have the following keys:
34
+ # * *symbolize_names*: If set to true, returns symbols for the names
35
+ # (keys) in a Lines object. Otherwise strings are returned. Strings are
36
+ # the default.
37
+ def parse(source, options = {})
38
+ opts = Lines.parse_default_options.merge(options.to_hash)
39
+ @parser.parse(source.to_str, opts)
136
40
  end
137
41
 
138
- # Wrapper object that holds a given context. Emitted by Lines.context
139
- class Context
140
- attr_reader :data
141
-
142
- def initialize(data)
143
- @data = data
144
- end
145
-
146
- # Works like the Lines.log method.
147
- def log(obj, args={})
148
- Lines.log obj, Lines.ensure_hash!(args).merge(data)
149
- end
42
+ # Generate a Lines string from the Ruby data structure _obj_ and return
43
+ # it.
44
+ #
45
+ # _options_ can have the following keys:
46
+ # * *max_nesting*: The maximum depth of nesting allowed in the data
47
+ # structures from which Lines is to be generated. It defaults to 4.
48
+ # * *max_bytesize*: The maximum number of bytes for a line to be constructed
49
+ # on. It defaults to 2048.
50
+ def generate(obj, options = {})
51
+ opts = Lines.generate_default_options.merge(options.to_hash)
52
+ @generator.generate(obj.to_hash, opts)
150
53
  end
151
54
 
152
- # Handles output to any kind of IO
153
- class StreamOutputter
154
- NL = "\n".freeze
155
-
156
- # stream must accept a #write(str) message
157
- def initialize(stream = $stderr)
158
- @stream = stream
159
- # Is this needed ?
160
- @stream.sync = true if @stream.respond_to?(:sync)
161
- end
162
-
163
- def output(dumper, obj)
164
- str = dumper.dump(obj) + NL
165
- @stream.write str
166
- end
55
+
56
+ # Load a ruby data structure from a Lines _source_ and return it. A source can
57
+ # either be a string-like object, an IO-like object, or an object responding
58
+ # to the read method. If _proc_ was given, it will be called with any nested
59
+ # Ruby object as an argument recursively in depth first order. To modify the
60
+ # default options pass in the optional _options_ argument as well.
61
+ #
62
+ # The default options for the parser can be changed via the
63
+ # parse_default_options method.
64
+ #
65
+ # This method is part of the implementation of the load/dump interface of
66
+ # Marshal and YAML.
67
+ def load(source, proc = nil, options = {})
68
+ if source.respond_to? :to_str
69
+ source = source.to_str
70
+ elsif source.respond_to? :to_io
71
+ source = source.to_io.read
72
+ elsif source.respond_to?(:read)
73
+ source = source.read
74
+ end
75
+ result = parse(source, options)
76
+ recurse_proc(result, &proc) if proc
77
+ result
167
78
  end
168
79
 
169
- require 'syslog'
170
- # Handles output to syslog
171
- class SyslogOutputter
172
- PRI2SYSLOG = {
173
- 'debug' => ::Syslog::LOG_DEBUG,
174
- 'info' => ::Syslog::LOG_INFO,
175
- 'warn' => ::Syslog::LOG_WARNING,
176
- 'warning' => ::Syslog::LOG_WARNING,
177
- 'err' => ::Syslog::LOG_ERR,
178
- 'error' => ::Syslog::LOG_ERR,
179
- 'crit' => ::Syslog::LOG_CRIT,
180
- 'critical' => ::Syslog::LOG_CRIT,
181
- }.freeze
182
-
183
- def initialize(syslog = ::Syslog)
184
- @syslog = syslog
185
- end
186
-
187
- def output(dumper, obj)
188
- prepare_syslog obj[:app]
189
-
190
- obj = obj.dup
191
- obj.delete(:pid) # It's going to be part of the message
192
- obj.delete(:at) # Also part of the message
193
- obj.delete(:app) # And again
194
-
195
- level = extract_pri(obj)
196
-
197
- @syslog.log(level, "%s", dumper.dump(obj))
198
- end
199
-
200
- protected
201
-
202
- def prepare_syslog(app_name)
203
- return if @syslog.opened?
204
- app_name ||= File.basename($0)
205
- @syslog.open(app_name,
206
- ::Syslog::LOG_PID | ::Syslog::LOG_CONS | ::Syslog::LOG_NDELAY,
207
- ::Syslog::LOG_USER)
208
- end
209
-
210
- def extract_pri(h)
211
- pri = h.delete(:pri).to_s.downcase
212
- PRI2SYSLOG[pri] || ::Syslog::LOG_INFO
80
+ # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
81
+ def recurse_proc(result, &proc)
82
+ case result
83
+ when Array
84
+ result.each { |x| recurse_proc x, &proc }
85
+ proc.call result
86
+ when Hash
87
+ result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
88
+ proc.call result
89
+ else
90
+ proc.call result
213
91
  end
214
92
  end
93
+ protected :recurse_proc
215
94
 
216
- # Some opinions here as well on the format:
217
- #
218
- # We really want to never fail at dumping because you know, they're logs.
219
- # It's better to get a slightly less readable log that no logs at all.
220
- #
221
- # We're trying to be helpful for humans. It means that if possible we want
222
- # to make things shorter and more readable. It also means that ideally
223
- # we would like the parsing to be isomorphic but approximations are alright.
224
- # For example a symbol might become a string.
225
- #
226
- # Basically, values are either composite (dictionaries and arrays), quoted
227
- # strings or litterals. Litterals are strings that can be parsed to
228
- # something else depending if the language supports it or not.
229
- # Litterals never contain white-spaces or other weird (very precise !) characters.
95
+ # Dumps _obj_ as a Lines string, i.e. calls generate on the object and returns
96
+ # the result.
230
97
  #
231
- # the true litteral is written as "#t"
232
- # the false litteral is written as "#f"
233
- # the nil / null litteral is written as "nil"
98
+ # If anIO (an IO-like object or an object that responds to the write method)
99
+ # was given, the resulting JSON is written to it.
234
100
  #
235
- # dictionary keys are always strings or litterals.
101
+ # If the number of nested arrays or objects exceeds _limit_, the object or
102
+ # array is replaced with {...} or [...] respectively.
103
+ # This argument is similar (but not exactly the same!) to the _limit_
104
+ # argument in Marshal.dump.
236
105
  #
237
- # Pleaaase, keep units with numbers. And we provide a way for this:
238
- # a tuple of (number, litteral) can be concatenated. Eg: (3, 'ms') => 3ms
239
- # alternatively if your language supports a time range it could be serialized
240
- # to the same value (and parsed back as well).
106
+ # The default options for the generator can be changed via the
107
+ # generate_default_options method.
241
108
  #
242
- # if we don't know how to serialize something we provide a language-specific
243
- # string of it and encode is at such.
244
- #
245
- # The output ought to use the UTF-8 encoding.
246
- #
247
- # This dumper has been inspired by the OkJSON gem (both formats look alike
248
- # after all).
249
- class Dumper
250
- SPACE = ' '
251
- LIT_TRUE = '#t'
252
- LIT_FALSE = '#f'
253
- LIT_NIL = 'nil'
254
- OPEN_BRACE = '{'
255
- SHUT_BRACE = '}'
256
- OPEN_BRACKET = '['
257
- SHUT_BRACKET = ']'
258
- SINGLE_QUOTE = "'"
259
- DOUBLE_QUOTE = '"'
260
-
261
- constants.each(&:freeze)
262
-
263
- def dump(obj) #=> String
264
- objenc_internal(obj)
265
- end
266
-
267
- # Used to introduce new ruby litterals.
268
- #
269
- # Usage:
270
- #
271
- # Point = Struct.new(:x, :y)
272
- # Lines.dumper.map(Point) do |p|
273
- # "#{p.x}x#{p.y}"
274
- # end
275
- #
276
- # Lines.log msg: Point.new(3, 5)
277
- # # logs: msg=3x5
278
- #
279
- def map(klass, &rule)
280
- @mapping[klass] = rule
281
- end
282
-
283
- # After a certain depth, arrays are replaced with [...] and objects with
284
- # {...}. Default is 4.
285
- attr_accessor :max_depth
286
-
287
- protected
288
-
289
- attr_reader :mapping
290
-
291
- def initialize
292
- @mapping = {}
293
- @max_depth = 4
294
- end
295
-
296
- def objenc_internal(x, depth=0)
297
- depth += 1
298
- if depth > max_depth
299
- '...'
300
- else
301
- x.map{|k,v| "#{keyenc(k)}=#{valenc(v, depth)}" }.join(SPACE)
302
- end
303
- end
304
-
305
- def keyenc(k)
306
- case k
307
- when String, Symbol then strenc(k)
308
- else
309
- strenc(k.inspect)
310
- end
311
- end
312
-
313
- def valenc(x, depth)
314
- case x
315
- when Hash then objenc(x, depth)
316
- when Array then arrenc(x, depth)
317
- when String, Symbol then strenc(x)
318
- when Numeric then numenc(x)
319
- when Time then timeenc(x)
320
- when Date then dateenc(x)
321
- when true then LIT_TRUE
322
- when false then LIT_FALSE
323
- when nil then LIT_NIL
324
- else
325
- litenc(x)
326
- end
327
- end
328
-
329
- def objenc(x, depth)
330
- OPEN_BRACE + objenc_internal(x, depth) + SHUT_BRACE
331
- end
332
-
333
- def arrenc(a, depth)
334
- depth += 1
335
- # num + unit. Eg: 3ms
336
- if a.size == 2 && a.first.kind_of?(Numeric) && is_literal?(a.last.to_s)
337
- "#{numenc(a.first)}:#{strenc(a.last)}"
338
- elsif depth > max_depth
339
- '[...]'
340
- else
341
- OPEN_BRACKET + a.map{|x| valenc(x, depth)}.join(' ') + SHUT_BRACKET
342
- end
343
- end
344
-
345
- def strenc(s)
346
- s = s.to_s
347
- unless is_literal?(s)
348
- s = s.inspect
349
- unless s[1..-2].include?(SINGLE_QUOTE)
350
- s.gsub!(SINGLE_QUOTE, "\\'")
351
- s.gsub!('\"', DOUBLE_QUOTE)
352
- s[0] = s[-1] = SINGLE_QUOTE
353
- end
354
- end
355
- s
356
- end
357
-
358
- def numenc(n)
359
- #case n
360
- # when Float
361
- # "%.3f" % n
362
- #else
363
- n.to_s
364
- #end
365
- end
366
-
367
- def litenc(x)
368
- klass = (x.class.ancestors & mapping.keys).first
369
- if klass
370
- mapping[klass].call(x)
371
- else
372
- strenc(x.inspect)
109
+ # This method is part of the implementation of the load/dump interface of
110
+ # Marshal and YAML.
111
+ def dump(obj, anIO = nil, limit = nil)
112
+ if anIO and limit.nil?
113
+ anIO = anIO.to_io if anIO.respond_to?(:to_io)
114
+ unless anIO.respond_to?(:write)
115
+ limit = anIO
116
+ anIO = nil
373
117
  end
374
- rescue
375
- klass = (class << x; self; end).ancestors.first
376
- strenc("#<#{klass}:0x#{x.__id__.to_s(16)}>")
377
- end
378
-
379
- def timeenc(t)
380
- t.utc.iso8601
381
- end
382
-
383
- def dateenc(d)
384
- d.iso8601
385
118
  end
386
-
387
- def is_literal?(s)
388
- !s.index(/[\s'"=:{}\[\]]/)
389
- end
390
-
391
- end
392
-
393
- require 'securerandom'
394
- module UniqueIDs
395
- # A small utility to generate unique IDs that are as short as possible.
396
- #
397
- # It's useful to link contextes together
398
- #
399
- # See http://preshing.com/20110504/hash-collision-probabilities
400
- def id(collision_chance=1.0/10e9, over_x_messages=10e3)
401
- # Assuming that the distribution is perfectly random
402
- # how many bits do we need so that the chance of collision over_x_messages
403
- # is lower thant collision_chance ?
404
- number_of_possible_numbers = (over_x_messages ** 2) / (2 * collision_chance)
405
- num_bytes = (Math.log2(number_of_possible_numbers) / 8).ceil
406
- SecureRandom.urlsafe_base64(num_bytes)
119
+ opts = {}
120
+ opts[:max_nesting] = limit if limit
121
+ result = generate(obj, opts)
122
+ if anIO
123
+ anIO.write result
124
+ anIO
125
+ else
126
+ result
407
127
  end
408
128
  end
409
- extend UniqueIDs
410
129
  end
411
-
412
- # default config
413
- Lines.use($stderr)