Class: Blodsband::Riak::List
- Inherits:
-
Object
- Object
- Blodsband::Riak::List
- Defined in:
- lib/blodsband/riak/list.rb
Overview
A concurrent linked list.
Defined Under Namespace
Classes: ActorDeletedError, ConcurrentUpdateError, Element
Instance Attribute Summary collapse
-
#bucket ⇒ Object
readonly
The Bucket this list lives in.
-
#key ⇒ Object
readonly
The key of this list in Riak.
Instance Method Summary collapse
-
#append(value) ⇒ Object
(also: #<<)
Append to the list.
-
#append_and_get_element(value) ⇒ Blodsband::Riak::List::Element
Append to the list.
- #append_element_after(element, new_node) ⇒ Object
-
#delete_element(element) ⇒ Object
Delete an element.
-
#each(&block) ⇒ Object
Execute code on each element in the list.
-
#empty? ⇒ true, false
Whether this list is empty.
-
#exists? ⇒ true, false
Whether this list exists in Riak.
-
#first ⇒ Object
The first element of this list.
-
#initialize(bucket, key) ⇒ List
constructor
Access a list in a bucket.
-
#last ⇒ Object
The last element of this list.
-
#pop ⇒ Object
The last element of this list after having removed it.
-
#prepend(value) ⇒ Object
Prepend to the list.
- #prepend_element_before(element, new_node) ⇒ Object
-
#reload ⇒ Object
Make sure this list gets reloaded from Riak.
-
#save ⇒ Object
Save this list.
-
#shift ⇒ Object
The first element of this list after having removed it.
-
#size ⇒ Integer
The size of this list.
- #stack_each(current, &block) ⇒ Object
-
#to_a ⇒ Array
This list as a ruby Array.
-
#to_s ⇒ String
A string representation of the list.
-
#validate ⇒ Object
Validate that this list is consistent.
Constructor Details
#initialize(bucket, key) ⇒ List
Access a list in a bucket.
309 310 311 312 |
# File 'lib/blodsband/riak/list.rb', line 309 def initialize(bucket, key) @bucket = bucket @key = key end |
Instance Attribute Details
#bucket ⇒ Object (readonly)
The Bucket this list lives in.
301 302 303 |
# File 'lib/blodsband/riak/list.rb', line 301 def bucket @bucket end |
#key ⇒ Object (readonly)
The key of this list in Riak.
297 298 299 |
# File 'lib/blodsband/riak/list.rb', line 297 def key @key end |
Instance Method Details
#append(value) ⇒ Object Also known as: <<
Append to the list.
480 481 482 |
# File 'lib/blodsband/riak/list.rb', line 480 def append(value) append_and_get_element(value).value end |
#append_and_get_element(value) ⇒ Blodsband::Riak::List::Element
Append to the list.
495 496 497 498 499 |
# File 'lib/blodsband/riak/list.rb', line 495 def append_and_get_element(value) new_node = Element.create(self, value) append_element(new_node) new_node end |
#append_element_after(element, new_node) ⇒ Object
524 525 526 |
# File 'lib/blodsband/riak/list.rb', line 524 def append_element_after(element, new_node) add_element(element, new_node, :execute_append_element_after, "append #{new_node.value} after #{element.value}") end |
#delete_element(element) ⇒ Object
Delete an element.
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 |
# File 'lib/blodsband/riak/list.rb', line 535 def delete_element(element) begin backlog << [:execute_delete_element, element.key, "delete #{element.value}"] save begin rollforward rescue ConcurrentUpdateError => e ensure return true end rescue ConcurrentUpdateError => e if !exists? raise ActorDeletedError.new(self, to_s) elsif element.exists? false end end end |
#each(&block) ⇒ Object
Execute code on each element in the list.
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
# File 'lib/blodsband/riak/list.rb', line 457 def each(&block) begin element = nil while size > 0 && !(element = Element.find(self, document["head"])).exists? reload end stack_each(element, &block) if size > 0 rescue ActorDeletedError => e if e.actor == self raise e else retry end end end |
#empty? ⇒ true, false
Returns whether this list is empty.
324 325 326 |
# File 'lib/blodsband/riak/list.rb', line 324 def empty? size == 0 end |
#exists? ⇒ true, false
Returns whether this list exists in Riak.
331 332 333 |
# File 'lib/blodsband/riak/list.rb', line 331 def exists? !key.nil? && !bucket.get(key).nil? end |
#first ⇒ Object
Returns the first element of this list.
384 385 386 387 388 389 390 |
# File 'lib/blodsband/riak/list.rb', line 384 def first if empty? nil else Element.find(self, document["head"]).value end end |
#last ⇒ Object
Returns the last element of this list.
395 396 397 398 399 400 401 |
# File 'lib/blodsband/riak/list.rb', line 395 def last if empty? nil else Element.find(self, document["tail"]).value end end |
#pop ⇒ Object
Returns the last element of this list after having removed it.
356 357 358 359 360 361 362 363 364 365 |
# File 'lib/blodsband/riak/list.rb', line 356 def pop if empty? nil else e = Element.find(self, document["tail"]) rval = e.value e.delete rval end end |
#prepend(value) ⇒ Object
Prepend to the list.
508 509 510 511 512 |
# File 'lib/blodsband/riak/list.rb', line 508 def prepend(value) new_node = Element.create(self, value) prepend_element(new_node) value end |
#prepend_element_before(element, new_node) ⇒ Object
517 518 519 |
# File 'lib/blodsband/riak/list.rb', line 517 def prepend_element_before(element, new_node) add_element(element, new_node, :execute_prepend_element_before, "prepend #{new_node.value} before #{element.value}") end |
#reload ⇒ Object
Make sure this list gets reloaded from Riak.
563 564 565 |
# File 'lib/blodsband/riak/list.rb', line 563 def reload @document = nil end |
#save ⇒ Object
Save this list.
410 411 412 413 414 415 416 417 418 |
# File 'lib/blodsband/riak/list.rb', line 410 def save curr = document @document = bucket.cas(key, document, document.vclock) if @document.nil? raise ConcurrentUpdateError.new(self, to_s) else #STDERR.puts("#{$$} succeeded saving #{@document.inspect}") end end |
#shift ⇒ Object
Returns the first element of this list after having removed it.
370 371 372 373 374 375 376 377 378 379 |
# File 'lib/blodsband/riak/list.rb', line 370 def shift if empty? nil else e = Element.find(self, document["head"]) rval = e.value e.delete rval end end |
#size ⇒ Integer
Returns the size of this list.
338 339 340 |
# File 'lib/blodsband/riak/list.rb', line 338 def size document["size"] ||= 0 end |
#stack_each(current, &block) ⇒ Object
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/blodsband/riak/list.rb', line 420 def stack_each(current, &block) stack = [] while current if current.exists? block.call(current.value, current) if current.next stack << current if stack.include?(current.next_element) raise "Loop detected! Wtf? #{stack.inspect}" else current = current.next_element end else current = nil end else if stack.empty? return else while !stack.empty? && !stack.last.next stack.pop end if stack.empty? current = nil else current = stack.last.next_element end end end end end |
#to_a ⇒ Array
Returns this list as a ruby Array.
345 346 347 348 349 350 351 |
# File 'lib/blodsband/riak/list.rb', line 345 def to_a rval = [] each do |e| rval << e end rval end |
#to_s ⇒ String
Returns a string representation of the list.
317 318 319 |
# File 'lib/blodsband/riak/list.rb', line 317 def to_s "#{self.class}:#{key}" end |
#validate ⇒ Object
Validate that this list is consistent.
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
# File 'lib/blodsband/riak/list.rb', line 572 def validate d = bucket.get(key) s = 0 p = nil pe = nil c = d["head"] seen = [] while c seen << c e = bucket.get(c) raise "#{e} (#{e..inspect}) doesn't point back to #{pe} (#{pe..inspect})" if p && e.["list-previous"] != p p = c pe = e s += 1 c = e.["list-next"] raise "#{e} (#{e..inspect}) is the the beginning of a loop: #{seen.inspect}" if seen.include?(c) seen << c end raise "#{d.inspect}['tail'] doesn't point to the de facto tail #{pe}" unless d["tail"] == p raise "#{d.inspect}['size'] doesn't show the de facto size #{s}" unless s == d["size"] end |