Project

save_queue

0.0
No commit activity in last 3 years
No release in over 3 years
Save Queue allows to push objects to other object's queue for a delayed save. Queue save will be triggered by object#save.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0
>= 2.6

Runtime

>= 0
 Project Readme

Save Queue

Build Status Gemnasium Build Status

Save Queue creates queue of objects inside holder object.
Queue will be saved and cleared after successfull holder object save.

Holder object is object that include SaveQueue.
Object in queue is any instance of any class, that include SaveQueue or respond to #save method.

There are plugins, that alter queue or holder object behavior, find them in 'save_queue/plugins' directory and read about them in Plugins section of readme

Contents

  1. Installation
  2. Contributing
  3. Usage
    • Getting started
    • Error handling
  4. Plugins
    • Dirty
    • Validation
    • Notification
  5. Creating your own Queues / TODO
  6. FAQ
  7. Requirements
  8. Compatibility
  9. Copyright

Installation

gem install save_queue

Contributing

Please help to improve this project!

See contributing guide for best practices
I am using gitflow. Develop branch is most featured and usually far above master.

Usage

Getting started

  1. Include SaveQueue:

     require 'save_queue'
    
     class Artice
       include SaveQueue
    
       def save
         puts "article saved!"
       end
     end
    
  2. Add SaveQueue to some other classes (or implement #save method in it):

     require 'save_queue'
    
     class Tag
       include SaveQueue
    
       def save
         puts "tag saved!"
       end
     end
    
  3. Add some functionality:

     class Artice
       def tags=(tag_objects)
         @tags = tag_objects
         save_queue.add_all tag_objects
       end
    
       def add_tag(tag)
         @tags ||= []
         @tags << tag
         save_queue.add tag # methods #<<, #push can be also used to add object to queue
         # You may also do
         # tag.save_queue << self
         # save_queue will not circle in this case
       end
     end
    
  4. Voila!

     article = Article.new
    
     # Create 3 tags and add them to the article
     article.tags =
       3.times.map do
         tag = Tag.new
         tag.should_receive(:save).once
    
         tag
       end
    
     # Add single tag
     tag = Tag.new
     tag.should_receive(:save).once
    
     article.add_tag tag
    
     # that will save article and all tags in this article if article.save
     # and all tag.save returns true.
     # You may also use #save! method, that will delegate to article.save! and
     # raise SaveQueue::FailedSaveError on fail
     article.save.should be_true
    
     # Output:
     # article saved!
     # tag saved!
     # tag saved!
     # tag saved!
     # tag saved!
    
     # empty the queue after successfull save
     article.save_queue.should be_empty
    
     article.save
     # Output:
     # article saved!
    
     # You may call save on queue explicitly:
     #
     # article.save_queue.save
     # article.save
    
  5. To save object only if it was changed, include Dirty module described below in plugins section.

  6. Continue reading for more details.

Error handling

SaveQueue assumes, that #save method returns true/false and #save! raise an Exception if save failed:

unless article.save
  # You may use article.save_queue.errors.any? or article.save_queue.errors.empty? also

  # returns a [Hash] that contains information about saving proccess:
  # @option [Array<Object>] :saved
  # @option [Object]        :failed
  # @option [Array<Object>] :pending
  article.save_queue.errors[:save]
end


begin
  article.save!
rescue SaveQueue::FailedSaveError => error

  # returns a [Hash] that contains information about saving proccess:
  # @option [Array<Object>] :saved
  # @option [Object]        :failed
  # @option [Array<Object>] :pending
  error.context

  article.save_queue.errors[:save] # also set
end

Queue is not cleared after fail. Possible to fix errors and rerun queue.save

Plugins

Any "extra" functionality goes into bundled plugins.

Dirty

SaveQueue::Plugins::Dirty module provide changes tracking functional.
In order to use it include this module and call #mark_as_changed method in mutator methods like this:

require "save_queue"
require "save_queue/plugins/dirty"

class Artice
  include SaveQueue
  include SaveQueue::Plugins::Dirty

  def initialize
    @attributes = {}
  end

  def change_attribute attr, value
    @attributes[attr] = value
    mark_as_changed # call this and object will be marked for a save
  end
end

To mark object as saved, call #mark_as_saved method. SaveQueue Dirty plugin will automatically call #mark_as_saved method after saving an object. This marks are used when SaveQueue calls #save.
Object will be saved only, if it #has_unsaved_changes? method returns true. There are some docs from spec tests:

#has_unsaved_changes?
  should return true for changed object
  should return false for unchanged object
  should return false for new object

If you have custom logic for marking objects dirty then you may want to overwrite #has_unsaved_changes? method, methods #mark_as_saved and #mark_as_changed in you class like this:

def has_unsaved_changes?
  dirty? # dirty is your custom method to determine has object unsaved_changes or not
end

def mark_as_saved
  # your custom methods
end

def mark_as_changed
  # your custom methods
end

Validation

To use validation include SaveQueue::Plugins::Validation and implement #valid? method.
Failed objects are stored in save_queue.errors[:validation] array.
save_queue.errors are empty if no errors occurs

require 'save_queue'
require 'save_queue/plugins/validation'

class Artice
  include SaveQueue
  include SaveQueue::Plugins::Validation

  # @return [boolean]
  def valid?
    true
  end

  # ...
end

There are specs for them:

SaveQueue::Plugins::Validation::Queue
  is empty
    should be valid
    #save
      should be true
    #save!
      should not raise Exception
  contains valid objects
    #save
      should be true
      should save all elements
    #save!
      should not raise Exception
      should save all elements
    #valid?
      should be true
      should not has any errors
    #validate!
      should not raise any exception
      should not has any errors
  contains invalid objects
    behaves like queue with invalid objects
      #save
        should be false
        should not save elements
      #save!
        should raise SaveQueue::FailedValidationError
        should not save elements
      #valid?
        should be false
        should set errors
      #validate!
        should raise SaveQueue::FailedValidationError exception
        should set errors
  contains mix of valid and invalid objects
    #save should call #valid?
    #save! should call #validate!
    behaves like queue with invalid objects
      #save
        should be false
        should not save elements
      #save!
        should raise SaveQueue::FailedValidationError
        should not save elements
      #valid?
        should be false
        should set errors
      #validate!
        should raise SaveQueue::FailedValidationError exception
        should set errors

Plugin adds SaveQueue::FailedValidationError:

# Note: queue was not saved. You dont need to do a cleanup
unless article.save then
  failed_objects = article.save_queue.errors[:validation]
end

begin
  article.save!
rescue SaveQueue::FailedValidationError => error
  # [Array<Object>]
  error.failed_objects

  article.save_queue.errors[:validation] # also set
end

To catch both save and validation errors use SaveQueue::Error:

begin
  article.save!
rescue SaveQueue::Error
  # do something
end

To not save an object if save_queue is invalid, add this condition to #save method:

def save
  return false unless save_queue.valid?
  #..
end

or add it to validation (to object#valid? method for example).
Note, that queue#valid? and queue#validate return true/false and queue#validate! raises SaveQueue::FailedValidationError exception. Queue is not creared if validation fails.

Notification

To use notification include SaveQueue::Plugins::Notification.
Holder object will be notified by #queue_changed_event method.
Overwrite this method to implement your functionality, for ex logging.

require 'save_queue/plugins/notification'

class Artice
  include SaveQueue
  include SaveQueue::Plugins::Notification
end

article = Article.new
article.save_queue << tag # this will trigger callback #queue_changed_event on article


class Artice
  def queue_changed_event(result, object)
    puts "queue was changed!"
  end
end

article = Article.new
article.save_queue.add tag # Outputs "queue was changed!"

Creating your own Queues

/ TODO

FAQ

Q: I use #write method to store object, how can i use SaveQueue?
A: You may implement #save method like this:

class Artice
  # @return [boolean]
  def save
    write
  end
end

Note that SaveQueue assumes, that #save method returns true/false and #save! raise an Exception if save failed.

Q: Where i can get more information?
A: See test specs for more details.
How?
clone git project by

git clone git://github.com/AlexParamonov/save_queue.git

cd into it and run bundle

cd save_queue
bundle

and then rake

rake

docs will be printed to your console :)

Requirements

  • hooks
  • rspec2 for testing

Compatibility

tested with Ruby

  • 1.8.7
  • 1.9.2
  • 1.9.3
  • jruby-18mode
  • jruby-19mode
  • rbx-19mode
  • rbx-18mode
  • ruby-head
  • ree

see build history

Copyright

Copyright © 2011-2012 Alexander N Paramonov. Released under the MIT License. See the LICENSE file for further details.