• Stars
    star
    128
  • Rank 271,253 (Top 6 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 14 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An ActiveRecord plugin for atomic archiving and unarchiving of object trees. Inspired by ActsAsParanoid and PermanentRecord

ActsAsArchival

Build Status Gem Version

Atomically archive object trees in your activerecord models.

We had the problem that acts_as_paranoid and similar plugins/gems always work on a record-by-record basis and made it very difficult to restore records atomically (or archive them, for that matter).

Because the archive and unarchive methods are in transactions, and every archival record involved gets the same archive number upon archiving, you can easily restore or remove an entire set of records without having to worry about partial deletion or restoration.

Additionally, other plugins generally screw with how destroy/delete work. We don't because we actually want to be able to destroy records.

Maintenance

You might read the commit logs and think "This must be abandonware! This hasn't been updated in 2y!" But! This is a mature project that solves a specific problem in ActiveRecord. It tends to only be updated when a new major version of ActiveRecord comes out and hence the infrequent updates.

Install

Gemfile:

gem "acts_as_archival"

Any models you want to be archival should have the columns archive_number (String) and archived_at (DateTime).

i.e. rails g migration AddAAAToPost archive_number archived_at:datetime

Any dependent-destroy AAA model associated to an AAA model will be archived with its parent.

If you're stuck on Rails 4.0x/3x/2x, check out the older tags/branches, which are no longer in active development.

Example

class Hole < ActiveRecord::Base
  acts_as_archival
  has_many :rats, dependent: :destroy
end

class Rat < ActiveRecord::Base
  acts_as_archival
end

Simple interactions & scopes

h = Hole.create                  #
h.archived?                      # => false
h.archive!                       # => true
h.archived?                      # => true
h.archive_number                 # => "b56876de48a5dcfe71b2c13eec15e4a2"
h.archived_at                    # => Thu, 01 Jan 2012 01:49:21 -0400
h.unarchive!                     # => true
h.archived?                      # => false
h.archive_number                 # => nil
h.archived_at                    # => nil

Associations

h = Hole.create                  #
r = h.rats.create                #
h.archive!                       # => true
h.archive_number                 # => "b56876de48a5dcfe71b2c13eec15e4a2"
r.archived_at                    # => Thu, 01 Jan 2012 01:52:12 -0400
r.archived?                      # => true
h.unarchive!                     # => true
h.archive_number                 # => nil
r.archived_at                    # => nil
r.archived?                      # => false

Relations

Hole.create!
Hole.create!
Hole.create!

holes = Hole.all

# All records in the relation will be archived with the same archive_number.
# Dependent/Destroy relationships will be archived, and callbacks will still be honored.
holes.archive_all!              # => [array of Hole records in the relation]

holes.first.archive_number      # => "b56876de48a5dcfe71b2c13eec15e4a2"
holes.last.archive_number       # => "b56876de48a5dcfe71b2c13eec15e4a2"

holes.unarchive_all!            # => [array of Hole records in the relation]

Scopes

h = Hole.create
Hole.archived.size               # => 0
Hole.unarchived.size             # => 1
h.archive!
Hole.archived.size               # => 1
Hole.unarchived.size             # => 0

Utility methods

h = Hole.create                  #
h.archival?                   # => true
Hole.archival?                # => true

Options

When defining an AAA model, it is is possible to make it unmodifiable when it is archived by passing readonly_when_archived: true to the acts_as_archival call in your model.

class CantTouchThis < ActiveRecord::Base
  acts_as_archival readonly_when_archived: true
end

record = CantTouchThis.create(foo: "bar")
record.archive!                              # => true
record.foo = "I want this to work"
record.save                                  # => false
record.errors.full_messages.first            # => "Cannot modify an archived record."

Callbacks

AAA models have four additional callbacks to do any necessary cleanup or other processing before and after archiving and unarchiving, and can additionally halt the archive callback chain.

class Hole < ActiveRecord::Base
  acts_as_archival

  # runs before #archive!
  before_archive :some_method_before_archiving

  # runs after #archive!
  after_archive :some_method_after_archiving

  # runs before #unarchive!
  before_unarchive :some_method_before_unarchiving

  # runs after #unarchive!
  after_unarchive :some_method_after_unarchiving

  # ... implement those methods
end

Halting the callback chain

  • Rails 4.2 - the callback method should return a false/nil value.
  • Rails 5.x - the callback should throw(:abort)/raise(:abort).

Caveats

  1. This will only work on associations that are dependent destroy. It should be trival to change that or make it optional.
  2. If you would like to work on this, you will need to setup sqlite on your development machine. Alternately, you can disable specific dev dependencies in the gemspec and test_helper and ask for help.

Testing

Running the tests should be as easy as:

script/setup                 # bundles, makes databases with permissions
rake                         # run tests on latest Rails
appraisal rake               # run tests on all versions of Rails

Check out more on appraisal if you need to add new versions of things or run into a version bug.

Help Wanted

We'd love to have your help making this better! If you have ideas for features this should implement or you think the code sucks, let us know. And PRs are greatly appreciated. 👍

Thanks

ActsAsParanoid and PermanentRecords were both inspirations for this:

Contributors

  • Joel Meador
  • Michael Kuehl
  • Matthew Gordon
  • Vojtech Salbaba
  • David Jones
  • Dave Woodward
  • Miles Sterrett
  • James Hill
  • Maarten Claes
  • Anthony Panozzo
  • Aaron Milam
  • Anton Rieder
  • Josh Menden
  • Sergey Gnuskov

Thanks!

Copyright (c) 2009-2017 Expected Behavior, LLC, released under the MIT license

More Repositories

1

cheddargetter_client_ruby

Cheddar Getter API Client for Ruby
Ruby
33
star
2

doc_raptor_gem

Provides a simple ruby wrapper around the DocRaptor API
Ruby
18
star
3

active_mailer

Email with business logic and history
Ruby
10
star
4

common-files

Shell
9
star
5

pirate_metrics_agent

Get to know your customers
Ruby
9
star
6

doc_raptor_examples

Various examples on how to use Doc Raptor
Ruby
8
star
7

php-docraptor

PHP consumer for the DocRaptor.com API
PHP
7
star
8

docraptor-jquery

JavaScript
7
star
9

fix_you_some_address_bar

Fixes those pesky incorrect urls in the address bar after an error-filled form submission in Rails.
Ruby
6
star
10

has_one_authenticated_resource

Allows for per-user credentials for ActiveResource connections
Ruby
4
star
11

production_data

Rails plugin to help manage developing against your production data
Ruby
4
star
12

myspaceid-sdk

The MyspaceID Ruby SDK
Ruby
4
star
13

gittycent

A GitHub wrapper in Ruby (OUT OF DATE)
Ruby
4
star
14

sms_reminder

Simple app using Twilio to subscribe for reminder text
Ruby
3
star
15

redmine_project_burndown_callbacks

Ruby
3
star
16

assertions

More Assertions for Test::Unit::Assertions
Ruby
2
star
17

cf_ruby_gems

Gems we want to include in development/test in bundler projects
Ruby
2
star
18

rexchange

RExchange is an easy to use Microsoft Exchange Server WebDAV communication wrapper . This is a fork of the rubyforge project
Ruby
1
star
19

unobtrusive_js_rails_3

Ruby
1
star
20

indyrb-actioncable

A demo for Indy.rb of Rails 5's action cable feature
Ruby
1
star
21

external_resque_worker

Easy way to manage one or more Resque processes during testing
Ruby
1
star
22

cucumber_culerity_step_definitions

Ruby
1
star
23

db_setup

plugin for our db_setup script
Ruby
1
star
24

twitter_live

part of a presentation of making twitter live
Ruby
1
star
25

pb_test_repo

test can
1
star
26

feed_scrape

Ruby
1
star
27

doc_raptor_brownbag

Ruby
1
star
28

eb_capistrano_extensions

Ruby
1
star
29

domainatrix

A cruel mistress that uses the public suffix domain list to dominate URLs by canonicalizing, finding the public suffix, and breaking them into their domain parts.
Ruby
1
star
30

web_server_config_generator

automatically generate web server config files for you projects directory
Ruby
1
star
31

coffeescript-demo

Indy.rb Feb Presentation
Ruby
1
star
32

readonly_party

A monkey patch on HTTParty to disable PUT/POST/DELETE
Ruby
1
star
33

twitter_live_gh_demo

1
star
34

eb-patches

Collection of common patches
Ruby
1
star
35

active_mailer_demo

Ruby
1
star
36

paperclip_with_unified_table

Ruby
1
star
37

indyrb

Ruby
1
star
38

testing_talk_demo

Ruby
1
star
39

lzo-ruby

C
1
star
40

rails_application_syntax_examples

Examples of the kind of things you can do with Rails application templates.
Ruby
1
star