lines 0.2.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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)