rubysl-pstore 1.0.0 → 2.0.0

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,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