storable 0.8.6 → 0.9.pre.RC2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/proc_source.rb CHANGED
@@ -1,113 +1,162 @@
1
- #--
1
+
2
+ #
2
3
  # Based on:
3
- # http://github.com/imedo/background
4
- #++
4
+ # https://github.com/imedo/background
5
+ # https://github.com/imedo/background_lite
6
+ # With improvements by:
7
+ # https://github.com/notro/storable
8
+ #
9
+
10
+ # RubyToken was removed in Ruby 2.7
11
+ if RUBY_VERSION < "2.7"
12
+ require 'irb/ruby-token'
13
+ else
14
+ require './lib/core_ext.rb'
15
+ end
5
16
 
6
- require 'stringio'
7
17
  require 'irb/ruby-lex'
8
- #SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
18
+ require 'pry'
19
+ require 'stringio'
20
+
21
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
22
+
9
23
 
10
24
  class ProcString < String
11
- attr_accessor :file, :lines, :arity, :kind
25
+ # Filename where the proc is defined
26
+ attr_accessor :file
27
+
28
+ # Range of lines where the proc is defined. e.g. (12..16)
29
+ attr_accessor :lines
30
+ attr_accessor :arity, :kind
31
+
32
+ # Return a Proc object
33
+ # If #lines and #file is specified, these are tied to the proc.
12
34
  def to_proc(kind="proc")
13
- result = eval("#{kind} #{self}")
35
+ if @file && @lines
36
+ raise "#lines must be a range" unless @lines.kind_of? Range
37
+ result = eval("#{kind} #{self}", binding, @file, @lines.min)
38
+ else
39
+ result = eval("#{kind} #{self}")
40
+ end
14
41
  result.source = self
15
42
  result
16
43
  end
44
+
45
+ # Return a lambda
17
46
  def to_lambda
18
- to_proc "lamda"
47
+ to_proc "lambda"
19
48
  end
20
49
  end
21
50
 
22
51
  class RubyToken::Token
23
-
24
- # These EXPR_BEG tokens don't have associated end tags
25
- FAKIES = [RubyToken::TkWHEN, RubyToken::TkELSIF, RubyToken::TkTHEN]
26
-
52
+ # These EXPR_BEG tokens don't have associated end tags
53
+ FAKIES = [
54
+ RubyToken::TkWHEN,
55
+ RubyToken::TkELSIF,
56
+ RubyToken::TkELSE,
57
+ RubyToken::TkTHEN,
58
+ ]
59
+
60
+ def name
61
+ @name ||= nil
62
+ end
63
+
27
64
  def open_tag?
28
- return false if @name.nil? || get_props.nil?
29
- a = (get_props[1] == RubyToken::EXPR_BEG) &&
30
- self.class.to_s !~ /_MOD/ && # ignore onliner if, unless, etc...
31
- !FAKIES.member?(self.class)
32
- a
65
+ return false if name.nil? || get_props.nil?
66
+ is_open = (
67
+ (get_props[1] == RubyToken::EXPR_BEG) &&
68
+ (self.class.to_s !~ /_MOD/) && # ignore onliner if, unless, etc...
69
+ (!FAKIES.member?(self.class))
70
+ )
71
+ is_open
33
72
  end
34
-
73
+
35
74
  def get_props
36
- RubyToken::TkReading2Token[@name]
75
+ RubyToken::TkReading2Token[name]
37
76
  end
38
-
77
+
39
78
  end
40
79
 
41
80
  # Based heavily on code from http://github.com/imedo/background
42
81
  # Big thanks to the imedo dev team!
43
82
  #
44
83
  module ProcSource
45
-
46
- def self.find(filename, start_line=0, block_only=true)
84
+ def self.find(filename, start_line=1, block_only=true)
47
85
  lines, lexer = nil, nil
48
86
  retried = 0
49
87
  loop do
50
88
  lines = get_lines(filename, start_line)
51
89
  return nil if lines.nil?
52
- #p [start_line, lines[0]]
53
90
  if !line_has_open?(lines.join) && start_line >= 0
54
- start_line -= 1 and retried +=1 and redo
91
+ start_line -= 1 and retried +=1 and redo
55
92
  end
56
93
  lexer = RubyLex.new
57
94
  lexer.set_input(StringIO.new(lines.join))
58
95
  break
59
96
  end
97
+
60
98
  stoken, etoken, nesting = nil, nil, 0
61
- while token = lexer.token
62
- n = token.instance_variable_get(:@name)
63
-
99
+
100
+ binding.pry
101
+ if RUBY_VERSION < "2.7"
102
+ tokens = lexer.instance_variable_get '@OP'
103
+ else
104
+ lexer.lex
105
+ tokens = lexer.instance_variable_get '@tokens'
106
+ end
107
+
108
+ # tokens.each
109
+
110
+ while (token = lexer.token) do
64
111
  if RubyToken::TkIDENTIFIER === token
65
- #nothing
112
+ # nothing
66
113
  elsif token.open_tag? || RubyToken::TkfLBRACE === token
67
114
  nesting += 1
68
115
  stoken = token if nesting == 1
69
116
  elsif RubyToken::TkEND === token || RubyToken::TkRBRACE === token
70
117
  if nesting == 1
71
- etoken = token
118
+ etoken = token
72
119
  break
73
120
  end
74
121
  nesting -= 1
122
+ elsif RubyToken::TkLBRACE === token
123
+ nesting += 1
75
124
  elsif RubyToken::TkBITOR === token && stoken
76
- #nothing
125
+ # nothing
77
126
  elsif RubyToken::TkNL === token && stoken && etoken
78
127
  break if nesting <= 0
79
128
  else
80
- #p token
129
+ # nothing
81
130
  end
82
131
  end
83
- # puts lines if etoken.nil?
132
+
84
133
  lines = lines[stoken.line_no-1 .. etoken.line_no-1]
85
-
86
- # Remove the crud before the block definition.
134
+
135
+ # Remove the crud before the block definition.
87
136
  if block_only
88
137
  spaces = lines.last.match(/^\s+/)[0] rescue ''
89
138
  lines[0] = spaces << lines[0][stoken.char_no .. -1]
90
139
  end
91
140
  ps = ProcString.new lines.join
92
- ps.file, ps.lines = filename, start_line .. start_line+etoken.line_no-1
93
-
141
+ ps.file = filename
142
+ ps.lines = start_line .. start_line+etoken.line_no-1
94
143
  ps
95
144
  end
96
-
145
+
97
146
  # A hack for Ruby 1.9, otherwise returns true.
98
147
  #
99
148
  # Ruby 1.9 returns an incorrect line number
100
149
  # when a block is specified with do/end. It
101
- # happens b/c the line number returned by
150
+ # happens b/c the line number returned by
102
151
  # Ruby 1.9 is based on the first line in the
103
152
  # block which contains a token (i.e. not a
104
- # new line or comment etc...).
153
+ # new line or comment etc...).
105
154
  #
106
- # NOTE: This won't work in cases where the
107
- # incorrect line also contains a "do".
155
+ # NOTE: This won't work in cases where the
156
+ # incorrect line also contains a "do".
108
157
  #
109
158
  def self.line_has_open?(str)
110
- return true unless RUBY_VERSION >= '1.9'
159
+ return true unless RUBY_VERSION >= '1.9' && RUBY_VERSION < '2.0'
111
160
  lexer = RubyLex.new
112
161
  lexer.set_input(StringIO.new(str))
113
162
  success = false
@@ -117,12 +166,14 @@ module ProcSource
117
166
  break
118
167
  when RubyToken::TkDO
119
168
  success = true
169
+ when RubyToken::TkfLBRACE
170
+ success = true
120
171
  when RubyToken::TkCONSTANT
121
- if token.instance_variable_get(:@name) == "Proc" &&
172
+ if token.name == "Proc" &&
122
173
  lexer.token.is_a?(RubyToken::TkDOT)
123
174
  method = lexer.token
124
175
  if method.is_a?(RubyToken::TkIDENTIFIER) &&
125
- method.instance_variable_get(:@name) == "new"
176
+ method.name == "new"
126
177
  success = true
127
178
  end
128
179
  end
@@ -130,20 +181,26 @@ module ProcSource
130
181
  end
131
182
  success
132
183
  end
133
-
134
-
135
- def self.get_lines(filename, start_line = 0)
184
+
185
+ def self.get_lines(filename, start_line = 1)
136
186
  case filename
137
187
  when nil
138
188
  nil
139
- when "(irb)" # special "(irb)" descriptor?
189
+
190
+ # We're in irb
191
+ when "(irb)"
140
192
  IRB.conf[:MAIN_CONTEXT].io.line(start_line .. -2)
141
- when /^\(eval.+\)$/ # special "(eval...)" descriptor?
193
+
194
+ # Or an eval
195
+ when /^\(eval.+\)$/
142
196
  EVAL_LINES__[filename][start_line .. -2]
143
- else # regular file
197
+
198
+ # Or most likely a .rb file
199
+ else
144
200
  # Ruby already parsed this file? (see disclaimer above)
145
201
  if defined?(SCRIPT_LINES__) && SCRIPT_LINES__[filename]
146
202
  SCRIPT_LINES__[filename][(start_line - 1) .. -1]
203
+
147
204
  # If the file exists we're going to try reading it in
148
205
  elsif File.exist?(filename)
149
206
  begin
@@ -156,24 +213,77 @@ module ProcSource
156
213
  end
157
214
  end
158
215
 
159
- class Proc #:nodoc:
160
- attr_reader :file, :line
216
+ class Proc # :nodoc:
161
217
  attr_writer :source
162
-
218
+ @@regexp = Regexp.new('^#<Proc:0x[0-9A-Fa-f]+@?\s*(.+):(\d+)(.+?)?>$')
219
+
163
220
  def source_descriptor
164
- unless @file && @line
165
- if md = /^#<Proc:0x[0-9A-Fa-f]+@(.+):(\d+)(.+?)?>$/.match(inspect)
166
- @file, @line = md.captures
167
- end
221
+ return [@file, @line] if @file && @line
222
+
223
+ source_location = nil
224
+ if RUBY_VERSION >= '2.7'
225
+ source_location = *self.source_location
226
+ else
227
+ inspection = inspect
228
+ md = @@regexp.match(inspection)
229
+ exmsg = 'Unable to parse proc inspect (%s)' % inspection
230
+ raise Exception, exmsg if md.nil?
231
+ source_location = md.captures
168
232
  end
169
- [@file, @line.to_i]
233
+
234
+ file, line = *source_location
235
+ @file, @line = [file, line.to_i]
170
236
  end
171
-
237
+
172
238
  def source
173
239
  @source ||= ProcSource.find(*self.source_descriptor)
174
240
  end
175
-
176
- # Create a Proc object from a string of Ruby code.
241
+
242
+ def line
243
+ source_descriptor
244
+ @line
245
+ end
246
+
247
+ def file
248
+ source_descriptor
249
+ @file
250
+ end
251
+
252
+ # Dump to Marshal format.
253
+ # p = Proc.new { false }
254
+ # Marshal.dump p
255
+ def _dump(limit)
256
+ raise "can't dump proc, #source is nil" if source.nil?
257
+ str = Marshal.dump(source)
258
+ str
259
+ end
260
+
261
+ # Load from Marshal format.
262
+ # p = Proc.new { false }
263
+ # Marshal.load Marshal.dump p
264
+ def self._load(str)
265
+ @source = Marshal.load(str)
266
+ @source.to_proc
267
+ end
268
+
269
+ # Dump to JSON string
270
+ def to_json(*args)
271
+ raise "can't serialize proc, #source is nil" if source.nil?
272
+ {
273
+ 'json_class' => self.class.name,
274
+ 'data' => [source.to_s, source.file, source.lines.min, source.lines.max]
275
+ }.to_json#(*args)
276
+ end
277
+
278
+ def self.json_create(o)
279
+ s, file, min, max = o['data']
280
+ ps = ProcString.new s
281
+ ps.file = file
282
+ ps.lines = (min..max)
283
+ ps.to_proc
284
+ end
285
+
286
+ # Create a Proc object from a string of Ruby code.
177
287
  # It's assumed the string contains do; end or { }.
178
288
  #
179
289
  # Proc.from_string("do; 2+2; end")
@@ -181,7 +291,7 @@ class Proc #:nodoc:
181
291
  def self.from_string(str)
182
292
  eval "Proc.new #{str}"
183
293
  end
184
-
294
+
185
295
  end
186
296
 
187
297
  if $0 == __FILE__
@@ -194,19 +304,18 @@ if $0 == __FILE__
194
304
  end
195
305
 
196
306
  a = Proc.new() { |a|
197
- puts "Hello Rudy2"
307
+ puts "Hello Rudy2"
198
308
  }
199
-
309
+
200
310
  b = Proc.new() do |b|
201
311
  puts { "Hello Rudy3" } if true
202
312
  end
203
-
313
+
204
314
  puts @blk.inspect, @blk.source
205
315
  puts [a.inspect, a.source]
206
316
  puts b.inspect, b.source
207
-
317
+
208
318
  proc = @blk.source.to_proc
209
319
  proc.call(1)
210
320
  end
211
321
 
212
-
data/lib/storable.rb CHANGED
@@ -35,14 +35,14 @@ class Storable
35
35
  require 'proc_source'
36
36
  require 'storable/orderedhash' if USE_ORDERED_HASH
37
37
  unless defined?(SUPPORTED_FORMATS) # We can assume all are defined
38
- VERSION = "0.8.5"
38
+ VERSION = "0.8.9"
39
39
  NICE_TIME_FORMAT = "%Y-%m-%d@%H:%M:%S".freeze
40
40
  SUPPORTED_FORMATS = [:tsv, :csv, :yaml, :json, :s, :string].freeze
41
41
  end
42
42
 
43
43
  @debug = false
44
44
  class << self
45
- attr_accessor :sensitive_fields, :field_names, :field_types, :debug
45
+ attr_accessor :sensitive_fields, :field_names, :field_types, :field_opts, :debug
46
46
  end
47
47
 
48
48
  # Passes along fields to inherited classes
@@ -66,37 +66,61 @@ class Storable
66
66
  # data is available by the standard accessors, class.product and class.product= etc...
67
67
  # The value of the field will be cast to the type (if provided) when read from a file.
68
68
  # The value is not touched when the type is not provided.
69
- def self.field(args={}, &processor)
69
+ def self.field(*args, &processor)
70
70
  # TODO: Examine casting from: http://codeforpeople.com/lib/ruby/fattr/fattr-1.0.3/
71
- args = {args => nil} unless args.kind_of?(Hash)
72
-
71
+ field_definitions = {}
72
+ if args.first.kind_of?(Hash)
73
+ args.first.each_pair do |fname,klass|
74
+ field_definitions[fname] = { :class => klass }
75
+ end
76
+ else
77
+ fname, opts = *args
78
+ if opts.nil?
79
+ field_definitions[fname] = {}
80
+ elsif Hash === opts
81
+ field_definitions[fname] = opts
82
+ else
83
+ raise ArgumentError, "Second argument must be a hash"
84
+ end
85
+ end
86
+
73
87
  self.field_names ||= []
74
88
  self.field_types ||= {}
75
- args.each_pair do |m,t|
76
- self.field_names << m
77
- self.field_types[m] = t unless t.nil?
89
+ self.field_opts ||= {}
90
+ field_definitions.each_pair do |fname,opts|
91
+ self.field_names << fname
92
+ self.field_opts[fname] = opts
93
+ self.field_types[fname] = opts[:class] unless opts[:class].nil?
78
94
 
79
95
  # This processor automatically converts a Proc object
80
96
  # to a String of its source.
81
- processor = proc_processor if t == Proc && processor.nil?
97
+ processor = proc_processor if opts[:class] == Proc && processor.nil?
82
98
 
83
99
  unless processor.nil?
84
- define_method("_storable_processor_#{m}", &processor)
100
+ define_method("_storable_processor_#{fname}", &processor)
85
101
  end
86
102
 
87
- if method_defined?(m) # don't redefine the getter method
88
- STDERR.puts "method exists: #{self}##{m}" if Storable.debug
103
+ if method_defined?(fname) # don't redefine the getter method
104
+ STDERR.puts "method exists: #{self}##{fname}" if Storable.debug
89
105
  else
90
- define_method(m) do
91
- instance_variable_get("@#{m}")
106
+ define_method(fname) do
107
+ ret = instance_variable_get("@#{fname}")
108
+ if ret.nil?
109
+ if opts[:default]
110
+ ret = opts[:default]
111
+ elsif opts[:meth]
112
+ ret = self.send(opts[:meth])
113
+ end
114
+ end
115
+ ret
92
116
  end
93
117
  end
94
118
 
95
- if method_defined?("#{m}=") # don't redefine the setter methods
96
- STDERR.puts "method exists: #{self}##{m}=" if Storable.debug
119
+ if method_defined?("#{fname}=") # don't redefine the setter methods
120
+ STDERR.puts "method exists: #{self}##{fname}=" if Storable.debug
97
121
  else
98
- define_method("#{m}=") do |val|
99
- instance_variable_set("@#{m}",val)
122
+ define_method("#{fname}=") do |val|
123
+ instance_variable_set("@#{fname}",val)
100
124
  end
101
125
  end
102
126
  end
@@ -145,15 +169,15 @@ class Storable
145
169
 
146
170
  # Returns an array of field names defined by self.field
147
171
  def field_names
148
- self.class.field_names
172
+ self.class.field_names #|| self.class.ancestors.first.field_names
149
173
  end
150
174
  # Returns an array of field types defined by self.field. Fields that did
151
175
  # not receive a type are set to nil.
152
176
  def field_types
153
- self.class.field_types
177
+ self.class.field_types #|| self.class.ancestors.first.field_types
154
178
  end
155
179
  def sensitive_fields
156
- self.class.sensitive_fields
180
+ self.class.sensitive_fields #|| self.class.ancestors.first.sensitive_fields
157
181
  end
158
182
 
159
183
  # Dump the object data to the given format.
@@ -200,7 +224,7 @@ class Storable
200
224
  def init *args
201
225
  from_array *args
202
226
  end
203
-
227
+
204
228
  def initialize *args
205
229
  init *args
206
230
  end
@@ -298,14 +322,16 @@ class Storable
298
322
  def to_hash
299
323
  preprocess if respond_to? :preprocess
300
324
  tmp = USE_ORDERED_HASH ? Storable::OrderedHash.new : {}
301
- field_names.each do |fname|
302
- next if sensitive? && self.class.sensitive_field?(fname)
303
- v = self.send(fname)
304
- v = process(fname, v) if has_processor?(fname)
305
- if Array === v
306
- v = v.collect { |v2| v2.kind_of?(Storable) ? v2.to_hash : v2 }
325
+ if field_names
326
+ field_names.each do |fname|
327
+ next if sensitive? && self.class.sensitive_field?(fname)
328
+ v = self.send(fname)
329
+ v = process(fname, v) if has_processor?(fname)
330
+ if Array === v
331
+ v = v.collect { |v2| v2.kind_of?(Storable) ? v2.to_hash : v2 }
332
+ end
333
+ tmp[fname] = v.kind_of?(Storable) ? v.to_hash : v
307
334
  end
308
- tmp[fname] = v.kind_of?(Storable) ? v.to_hash : v
309
335
  end
310
336
  tmp
311
337
  end
@@ -329,9 +355,6 @@ class Storable
329
355
  hash = to_hash
330
356
  if YAJL_LOADED # set by Storable
331
357
  ret = Yajl::Encoder.encode(hash)
332
- #raise "DELANO"
333
- #ret.force_encoding("ISO-8859-1")
334
- #p [:to, ret.encoding.name] if ret.respond_to?(:encoding)
335
358
  ret
336
359
  elsif JSON_LOADED
337
360
  JSON.generate(hash, *from, &blk)
@@ -508,7 +531,8 @@ class Storable
508
531
  end
509
532
  def proc_processor
510
533
  Proc.new do |val|
511
- (Proc === val) ? val.source : val
534
+ ret = (Proc === val) ? val.source : val
535
+ ret
512
536
  end
513
537
  end
514
538
  # If the object already has a value for +@id+