rubysl-pstore 1.0.0 → 2.0.0

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
  SHA1:
3
- metadata.gz: 49e8c464bdca0f27119a2c5e04523932784880e7
4
- data.tar.gz: 4e10e4cf5ec7e64f77589b6918b4bb66aadba2d8
3
+ metadata.gz: 24fe72b1e61ec8763d53e14ec608eb963d9e049b
4
+ data.tar.gz: b45b7683494348f8980131dff596fd4432c59231
5
5
  SHA512:
6
- metadata.gz: c0194a5e9c670355a299c6cf3c78c74711ede3aef8f2446c2231a3216371256bb3287be3458766ce9e396124cc91841666ecd2fcaaefc4b7e90ac5339efedd52
7
- data.tar.gz: d71099fd0bd5172c5ad39ab78f771d4904f6270b8447ead45958e533d1648fcc19c4cf3b284a45f0ade1d1f944db3c2c264dfc7e960dae8acab084523c5c6261
6
+ metadata.gz: 255a2feb817a01b585498b9305bb46216d3dc5dd57c2ace302358cf02d1052a6174e5f9c18a760d2485b00d5736056c8e1da14c277f354c7a2d9f9d6adb53583
7
+ data.tar.gz: d46f755a5168f42e7aaee2083314b1a7a03aa35594e838df4612241e1948809729746c804d581f19c51de8ede835242eade7c947673cb694c665ce5bf31d4e3f
@@ -1,8 +1,7 @@
1
1
  language: ruby
2
- before_install:
3
- - gem update --system
4
- - gem --version
5
- - gem install rubysl-bundler
6
- script: bundle exec mspec spec
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
7
5
  rvm:
8
- - rbx-nightly-18mode
6
+ - 1.9.3
7
+ - rbx-nightly-19mode
@@ -3,59 +3,61 @@
3
3
  # pstore.rb -
4
4
  # originally by matz
5
5
  # documentation by Kev Jackson and James Edward Gray II
6
+ # improved by Hongli Lai
6
7
  #
7
8
  # See PStore for documentation.
8
9
 
9
10
 
10
11
  require "fileutils"
11
12
  require "digest/md5"
13
+ require "thread"
12
14
 
13
15
  #
14
16
  # PStore implements a file based persistence mechanism based on a Hash. User
15
17
  # code can store hierarchies of Ruby objects (values) into the data store file
16
- # by name (keys). An object hierarchy may be just a single object. User code
18
+ # by name (keys). An object hierarchy may be just a single object. User code
17
19
  # may later read values back from the data store or even update data, as needed.
18
- #
20
+ #
19
21
  # The transactional behavior ensures that any changes succeed or fail together.
20
22
  # This can be used to ensure that the data store is not left in a transitory
21
23
  # state, where some values were updated but others were not.
22
- #
23
- # Behind the scenes, Ruby objects are stored to the data store file with
24
- # Marshal. That carries the usual limitations. Proc objects cannot be
24
+ #
25
+ # Behind the scenes, Ruby objects are stored to the data store file with
26
+ # Marshal. That carries the usual limitations. Proc objects cannot be
25
27
  # marshalled, for example.
26
28
  #
27
29
  # == Usage example:
28
- #
30
+ #
29
31
  # require "pstore"
30
- #
32
+ #
31
33
  # # a mock wiki object...
32
34
  # class WikiPage
33
35
  # def initialize( page_name, author, contents )
34
36
  # @page_name = page_name
35
37
  # @revisions = Array.new
36
- #
38
+ #
37
39
  # add_revision(author, contents)
38
40
  # end
39
- #
41
+ #
40
42
  # attr_reader :page_name
41
- #
43
+ #
42
44
  # def add_revision( author, contents )
43
45
  # @revisions << { :created => Time.now,
44
46
  # :author => author,
45
47
  # :contents => contents }
46
48
  # end
47
- #
49
+ #
48
50
  # def wiki_page_references
49
51
  # [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/)
50
52
  # end
51
- #
53
+ #
52
54
  # # ...
53
55
  # end
54
- #
56
+ #
55
57
  # # create a new page...
56
58
  # home_page = WikiPage.new( "HomePage", "James Edward Gray II",
57
59
  # "A page about the JoysOfDocumentation..." )
58
- #
60
+ #
59
61
  # # then we want to update page data and the index together, or not at all...
60
62
  # wiki = PStore.new("wiki_pages.pstore")
61
63
  # wiki.transaction do # begin transaction; do all of this or none of it
@@ -66,9 +68,9 @@ require "digest/md5"
66
68
  # # update wiki index...
67
69
  # wiki[:wiki_index].push(*home_page.wiki_page_references)
68
70
  # end # commit changes to wiki data store file
69
- #
71
+ #
70
72
  # ### Some time later... ###
71
- #
73
+ #
72
74
  # # read wiki data...
73
75
  # wiki.transaction(true) do # begin read-only transaction, no changes allowed
74
76
  # wiki.roots.each do |data_root_name|
@@ -77,6 +79,20 @@ require "digest/md5"
77
79
  # end
78
80
  # end
79
81
  #
82
+ # == Transaction modes
83
+ #
84
+ # By default, file integrity is only ensured as long as the operating system
85
+ # (and the underlying hardware) doesn't raise any unexpected I/O errors. If an
86
+ # I/O error occurs while PStore is writing to its file, then the file will
87
+ # become corrupted.
88
+ #
89
+ # You can prevent this by setting <em>pstore.ultra_safe = true</em>.
90
+ # However, this results in a minor performance loss, and only works on platforms
91
+ # that support atomic file renames. Please consult the documentation for
92
+ # +ultra_safe+ for details.
93
+ #
94
+ # Needless to say, if you're storing valuable data with PStore, then you should
95
+ # backup the PStore files from time to time.
80
96
  class PStore
81
97
  binmode = defined?(File::BINARY) ? File::BINARY : 0
82
98
  RDWR_ACCESS = File::RDWR | File::CREAT | binmode
@@ -87,11 +103,23 @@ class PStore
87
103
  class Error < StandardError
88
104
  end
89
105
 
90
- #
91
- # To construct a PStore object, pass in the _file_ path where you would like
106
+ # Whether PStore should do its best to prevent file corruptions, even when under
107
+ # unlikely-to-occur error conditions such as out-of-space conditions and other
108
+ # unusual OS filesystem errors. Setting this flag comes at the price in the form
109
+ # of a performance loss.
110
+ #
111
+ # This flag only has effect on platforms on which file renames are atomic (e.g.
112
+ # all POSIX platforms: Linux, MacOS X, FreeBSD, etc). The default value is false.
113
+ attr_accessor :ultra_safe
114
+
115
+ #
116
+ # To construct a PStore object, pass in the _file_ path where you would like
92
117
  # the data to be stored.
93
- #
94
- def initialize(file)
118
+ #
119
+ # PStore objects are always reentrant. But if _thread_safe_ is set to true,
120
+ # then it will become thread-safe at the cost of a minor performance hit.
121
+ #
122
+ def initialize(file, thread_safe = false)
95
123
  dir = File::dirname(file)
96
124
  unless File::directory? dir
97
125
  raise PStore::Error, format("directory %s does not exist", dir)
@@ -99,19 +127,21 @@ class PStore
99
127
  if File::exist? file and not File::readable? file
100
128
  raise PStore::Error, format("file %s not readable", file)
101
129
  end
102
- @transaction = false
103
130
  @filename = file
104
131
  @abort = false
132
+ @ultra_safe = false
133
+ @thread_safe = thread_safe
134
+ @lock = Mutex.new
105
135
  end
106
136
 
107
137
  # Raises PStore::Error if the calling code is not in a PStore#transaction.
108
138
  def in_transaction
109
- raise PStore::Error, "not in transaction" unless @transaction
139
+ raise PStore::Error, "not in transaction" unless @lock.locked?
110
140
  end
111
- #
141
+ #
112
142
  # Raises PStore::Error if the calling code is not in a PStore#transaction or
113
143
  # if the code is in a read-only PStore#transaction.
114
- #
144
+ #
115
145
  def in_transaction_wr()
116
146
  in_transaction()
117
147
  raise PStore::Error, "in read-only transaction" if @rdonly
@@ -119,9 +149,9 @@ class PStore
119
149
  private :in_transaction, :in_transaction_wr
120
150
 
121
151
  #
122
- # Retrieves a value from the PStore file data, by _name_. The hierarchy of
152
+ # Retrieves a value from the PStore file data, by _name_. The hierarchy of
123
153
  # Ruby objects stored under that root _name_ will be returned.
124
- #
154
+ #
125
155
  # *WARNING*: This method is only valid in a PStore#transaction. It will
126
156
  # raise PStore::Error if called at any other time.
127
157
  #
@@ -130,22 +160,22 @@ class PStore
130
160
  @table[name]
131
161
  end
132
162
  #
133
- # This method is just like PStore#[], save that you may also provide a
134
- # _default_ value for the object. In the event the specified _name_ is not
135
- # found in the data store, your _default_ will be returned instead. If you do
136
- # not specify a default, PStore::Error will be raised if the object is not
163
+ # This method is just like PStore#[], save that you may also provide a
164
+ # _default_ value for the object. In the event the specified _name_ is not
165
+ # found in the data store, your _default_ will be returned instead. If you do
166
+ # not specify a default, PStore::Error will be raised if the object is not
137
167
  # found.
138
- #
168
+ #
139
169
  # *WARNING*: This method is only valid in a PStore#transaction. It will
140
170
  # raise PStore::Error if called at any other time.
141
171
  #
142
172
  def fetch(name, default=PStore::Error)
143
173
  in_transaction
144
174
  unless @table.key? name
145
- if default==PStore::Error
146
- raise PStore::Error, format("undefined root name `%s'", name)
175
+ if default == PStore::Error
176
+ raise PStore::Error, format("undefined root name `%s'", name)
147
177
  else
148
- return default
178
+ return default
149
179
  end
150
180
  end
151
181
  @table[name]
@@ -154,11 +184,11 @@ class PStore
154
184
  # Stores an individual Ruby object or a hierarchy of Ruby objects in the data
155
185
  # store file under the root _name_. Assigning to a _name_ already in the data
156
186
  # store clobbers the old data.
157
- #
187
+ #
158
188
  # == Example:
159
- #
189
+ #
160
190
  # require "pstore"
161
- #
191
+ #
162
192
  # store = PStore.new("data_file.pstore")
163
193
  # store.transaction do # begin transaction
164
194
  # # load some data into the store...
@@ -166,7 +196,7 @@ class PStore
166
196
  # store[:obj_heirarchy] = { "Kev Jackson" => ["rational.rb", "pstore.rb"],
167
197
  # "James Gray" => ["erb.rb", "pstore.rb"] }
168
198
  # end # commit changes to data store file
169
- #
199
+ #
170
200
  # *WARNING*: This method is only valid in a PStore#transaction and it cannot
171
201
  # be read-only. It will raise PStore::Error if called at any other time.
172
202
  #
@@ -176,7 +206,7 @@ class PStore
176
206
  end
177
207
  #
178
208
  # Removes an object hierarchy from the data store, by _name_.
179
- #
209
+ #
180
210
  # *WARNING*: This method is only valid in a PStore#transaction and it cannot
181
211
  # be read-only. It will raise PStore::Error if called at any other time.
182
212
  #
@@ -187,7 +217,7 @@ class PStore
187
217
 
188
218
  #
189
219
  # Returns the names of all object hierarchies currently in the store.
190
- #
220
+ #
191
221
  # *WARNING*: This method is only valid in a PStore#transaction. It will
192
222
  # raise PStore::Error if called at any other time.
193
223
  #
@@ -197,7 +227,7 @@ class PStore
197
227
  end
198
228
  #
199
229
  # Returns true if the supplied _name_ is currently in the data store.
200
- #
230
+ #
201
231
  # *WARNING*: This method is only valid in a PStore#transaction. It will
202
232
  # raise PStore::Error if called at any other time.
203
233
  #
@@ -213,22 +243,22 @@ class PStore
213
243
  #
214
244
  # Ends the current PStore#transaction, committing any changes to the data
215
245
  # store immediately.
216
- #
246
+ #
217
247
  # == Example:
218
- #
248
+ #
219
249
  # require "pstore"
220
- #
250
+ #
221
251
  # store = PStore.new("data_file.pstore")
222
252
  # store.transaction do # begin transaction
223
253
  # # load some data into the store...
224
254
  # store[:one] = 1
225
255
  # store[:two] = 2
226
- #
256
+ #
227
257
  # store.commit # end transaction here, committing changes
228
- #
258
+ #
229
259
  # store[:three] = 3 # this change is never reached
230
260
  # end
231
- #
261
+ #
232
262
  # *WARNING*: This method is only valid in a PStore#transaction. It will
233
263
  # raise PStore::Error if called at any other time.
234
264
  #
@@ -240,21 +270,21 @@ class PStore
240
270
  #
241
271
  # Ends the current PStore#transaction, discarding any changes to the data
242
272
  # store.
243
- #
273
+ #
244
274
  # == Example:
245
- #
275
+ #
246
276
  # require "pstore"
247
- #
277
+ #
248
278
  # store = PStore.new("data_file.pstore")
249
279
  # store.transaction do # begin transaction
250
280
  # store[:one] = 1 # this change is not applied, see below...
251
281
  # store[:two] = 2 # this change is not applied, see below...
252
- #
282
+ #
253
283
  # store.abort # end transaction here, discard all changes
254
- #
284
+ #
255
285
  # store[:three] = 3 # this change is never reached
256
286
  # end
257
- #
287
+ #
258
288
  # *WARNING*: This method is only valid in a PStore#transaction. It will
259
289
  # raise PStore::Error if called at any other time.
260
290
  #
@@ -266,109 +296,216 @@ class PStore
266
296
 
267
297
  #
268
298
  # Opens a new transaction for the data store. Code executed inside a block
269
- # passed to this method may read and write data to and from the data store
299
+ # passed to this method may read and write data to and from the data store
270
300
  # file.
271
- #
301
+ #
272
302
  # At the end of the block, changes are committed to the data store
273
- # automatically. You may exit the transaction early with a call to either
303
+ # automatically. You may exit the transaction early with a call to either
274
304
  # PStore#commit or PStore#abort. See those methods for details about how
275
- # changes are handled. Raising an uncaught Exception in the block is
305
+ # changes are handled. Raising an uncaught Exception in the block is
276
306
  # equivalent to calling PStore#abort.
277
- #
307
+ #
278
308
  # If _read_only_ is set to +true+, you will only be allowed to read from the
279
309
  # data store during the transaction and any attempts to change the data will
280
310
  # raise a PStore::Error.
281
- #
311
+ #
282
312
  # Note that PStore does not support nested transactions.
283
313
  #
284
- def transaction(read_only=false) # :yields: pstore
285
- raise PStore::Error, "nested transaction" if @transaction
286
- begin
314
+ def transaction(read_only = false, &block) # :yields: pstore
315
+ value = nil
316
+ raise PStore::Error, "nested transaction" if !@thread_safe && @lock.locked?
317
+ @lock.synchronize do
287
318
  @rdonly = read_only
288
319
  @abort = false
289
- @transaction = true
290
- value = nil
291
- new_file = @filename + ".new"
320
+ file = open_and_lock_file(@filename, read_only)
321
+ if file
322
+ begin
323
+ @table, checksum, original_data_size = load_data(file, read_only)
292
324
 
293
- content = nil
294
- unless read_only
295
- file = File.open(@filename, RDWR_ACCESS)
296
- file.flock(File::LOCK_EX)
297
- commit_new(file) if FileTest.exist?(new_file)
298
- content = file.read()
325
+ catch(:pstore_abort_transaction) do
326
+ value = yield(self)
327
+ end
328
+
329
+ if !@abort && !read_only
330
+ save_data(checksum, original_data_size, file)
331
+ end
332
+ ensure
333
+ file.close if !file.closed?
334
+ end
299
335
  else
336
+ # This can only occur if read_only == true.
337
+ @table = {}
338
+ catch(:pstore_abort_transaction) do
339
+ value = yield(self)
340
+ end
341
+ end
342
+ end
343
+ value
344
+ rescue ThreadError
345
+ raise PStore::Error, "nested transaction"
346
+ end
347
+
348
+ private
349
+ # Constant for relieving Ruby's garbage collector.
350
+ EMPTY_STRING = ""
351
+ EMPTY_MARSHAL_DATA = Marshal.dump({})
352
+ EMPTY_MARSHAL_CHECKSUM = Digest::MD5.digest(EMPTY_MARSHAL_DATA)
353
+
354
+ #
355
+ # Open the specified filename (either in read-only mode or in
356
+ # read-write mode) and lock it for reading or writing.
357
+ #
358
+ # The opened File object will be returned. If _read_only_ is true,
359
+ # and the file does not exist, then nil will be returned.
360
+ #
361
+ # All exceptions are propagated.
362
+ #
363
+ def open_and_lock_file(filename, read_only)
364
+ if read_only
365
+ begin
366
+ file = File.new(filename, RD_ACCESS)
300
367
  begin
301
- file = File.open(@filename, RD_ACCESS)
302
368
  file.flock(File::LOCK_SH)
303
- content = (File.open(new_file, RD_ACCESS) {|n| n.read} rescue file.read())
304
- rescue Errno::ENOENT
305
- content = ""
369
+ return file
370
+ rescue
371
+ file.close
372
+ raise
306
373
  end
374
+ rescue Errno::ENOENT
375
+ return nil
307
376
  end
377
+ else
378
+ file = File.new(filename, RDWR_ACCESS)
379
+ file.flock(File::LOCK_EX)
380
+ return file
381
+ end
382
+ end
308
383
 
309
- if content != ""
310
- @table = load(content)
311
- if !read_only
312
- size = content.size
313
- md5 = Digest::MD5.digest(content)
384
+ # Load the given PStore file.
385
+ # If +read_only+ is true, the unmarshalled Hash will be returned.
386
+ # If +read_only+ is false, a 3-tuple will be returned: the unmarshalled
387
+ # Hash, an MD5 checksum of the data, and the size of the data.
388
+ def load_data(file, read_only)
389
+ if read_only
390
+ begin
391
+ table = load(file)
392
+ if !table.is_a?(Hash)
393
+ raise Error, "PStore file seems to be corrupted."
314
394
  end
395
+ rescue EOFError
396
+ # This seems to be a newly-created file.
397
+ table = {}
398
+ end
399
+ table
400
+ else
401
+ data = file.read
402
+ if data.empty?
403
+ # This seems to be a newly-created file.
404
+ table = {}
405
+ checksum = empty_marshal_checksum
406
+ size = empty_marshal_data.size
315
407
  else
316
- @table = {}
408
+ table = load(data)
409
+ checksum = Digest::MD5.digest(data)
410
+ size = data.size
411
+ if !table.is_a?(Hash)
412
+ raise Error, "PStore file seems to be corrupted."
413
+ end
317
414
  end
318
- content = nil # unreference huge data
415
+ data.replace(EMPTY_STRING)
416
+ [table, checksum, size]
417
+ end
418
+ end
319
419
 
320
- begin
321
- catch(:pstore_abort_transaction) do
322
- value = yield(self)
323
- end
324
- rescue Exception
325
- @abort = true
326
- raise
327
- ensure
328
- if !read_only and !@abort
329
- tmp_file = @filename + ".tmp"
330
- content = dump(@table)
331
- if !md5 || size != content.size || md5 != Digest::MD5.digest(content)
332
- File.open(tmp_file, WR_ACCESS) {|t| t.write(content)}
333
- File.rename(tmp_file, new_file)
334
- commit_new(file)
335
- end
336
- content = nil # unreference huge data
337
- end
420
+ def on_windows?
421
+ is_windows = RUBY_PLATFORM =~ /mswin/ ||
422
+ RUBY_PLATFORM =~ /mingw/ ||
423
+ RUBY_PLATFORM =~ /bccwin/ ||
424
+ RUBY_PLATFORM =~ /wince/
425
+ self.class.__send__(:define_method, :on_windows?) do
426
+ is_windows
427
+ end
428
+ is_windows
429
+ end
430
+
431
+ # Check whether Marshal.dump supports the 'canonical' option. This option
432
+ # makes sure that Marshal.dump always dumps data structures in the same order.
433
+ # This is important because otherwise, the checksums that we generate may differ.
434
+ def marshal_dump_supports_canonical_option?
435
+ begin
436
+ Marshal.dump(nil, -1, true)
437
+ result = true
438
+ rescue
439
+ result = false
440
+ end
441
+ self.class.__send__(:define_method, :marshal_dump_supports_canonical_option?) do
442
+ result
443
+ end
444
+ result
445
+ end
446
+
447
+ def save_data(original_checksum, original_file_size, file)
448
+ # We only want to save the new data if the size or checksum has changed.
449
+ # This results in less filesystem calls, which is good for performance.
450
+ if marshal_dump_supports_canonical_option?
451
+ new_data = Marshal.dump(@table, -1, true)
452
+ else
453
+ new_data = dump(@table)
454
+ end
455
+ new_checksum = Digest::MD5.digest(new_data)
456
+
457
+ if new_data.size != original_file_size || new_checksum != original_checksum
458
+ if @ultra_safe && !on_windows?
459
+ # Windows doesn't support atomic file renames.
460
+ save_data_with_atomic_file_rename_strategy(new_data, file)
461
+ else
462
+ save_data_with_fast_strategy(new_data, file)
338
463
  end
464
+ end
465
+
466
+ new_data.replace(EMPTY_STRING)
467
+ end
468
+
469
+ def save_data_with_atomic_file_rename_strategy(data, file)
470
+ temp_filename = "#{@filename}.tmp.#{Process.pid}.#{rand 1000000}"
471
+ temp_file = File.new(temp_filename, WR_ACCESS)
472
+ begin
473
+ temp_file.flock(File::LOCK_EX)
474
+ temp_file.write(data)
475
+ temp_file.flush
476
+ File.rename(temp_filename, @filename)
477
+ rescue
478
+ File.unlink(temp_file) rescue nil
479
+ raise
339
480
  ensure
340
- @table = nil
341
- @transaction = false
342
- file.close if file
481
+ temp_file.close
343
482
  end
344
- value
345
483
  end
346
484
 
347
- # This method is just a wrapped around Marshal.dump.
485
+ def save_data_with_fast_strategy(data, file)
486
+ file.rewind
487
+ file.truncate(0)
488
+ file.write(data)
489
+ end
490
+
491
+
492
+ # This method is just a wrapped around Marshal.dump
493
+ # to allow subclass overriding used in YAML::Store.
348
494
  def dump(table) # :nodoc:
349
495
  Marshal::dump(table)
350
496
  end
351
497
 
352
498
  # This method is just a wrapped around Marshal.load.
499
+ # to allow subclass overriding used in YAML::Store.
353
500
  def load(content) # :nodoc:
354
501
  Marshal::load(content)
355
502
  end
356
503
 
357
- # This method is just a wrapped around Marshal.load.
358
- def load_file(file) # :nodoc:
359
- Marshal::load(file)
504
+ def empty_marshal_data
505
+ EMPTY_MARSHAL_DATA
360
506
  end
361
-
362
- private
363
- # Commits changes to the data store file.
364
- def commit_new(f)
365
- f.truncate(0)
366
- f.rewind
367
- new_file = @filename + ".new"
368
- File.open(new_file, RD_ACCESS) do |nf|
369
- FileUtils.copy_stream(nf, f)
370
- end
371
- File.unlink(new_file)
507
+ def empty_marshal_checksum
508
+ EMPTY_MARSHAL_CHECKSUM
372
509
  end
373
510
  end
374
511
 
@@ -1,5 +1,5 @@
1
1
  module RubySL
2
2
  module PStore
3
- VERSION = "1.0.0"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
@@ -19,5 +19,4 @@ Gem::Specification.new do |spec|
19
19
  spec.add_development_dependency "bundler", "~> 1.3"
20
20
  spec.add_development_dependency "rake", "~> 10.0"
21
21
  spec.add_development_dependency "mspec", "~> 1.5"
22
- spec.add_development_dependency "rubysl-prettyprint", "~> 1.0"
23
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubysl-pstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Shirai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-26 00:00:00.000000000 Z
11
+ date: 2013-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.5'
55
- - !ruby/object:Gem::Dependency
56
- name: rubysl-prettyprint
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ~>
60
- - !ruby/object:Gem::Version
61
- version: '1.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ~>
67
- - !ruby/object:Gem::Version
68
- version: '1.0'
69
55
  description: Ruby standard library pstore.
70
56
  email:
71
57
  - brixen@gmail.com