activerecord-archiver 0.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 +7 -0
- data/lib/activerecord-archiver/archiver.rb +79 -0
- data/lib/activerecord-archiver/export.rb +86 -0
- data/lib/activerecord-archiver/import.rb +51 -0
- data/lib/activerecord-archiver.rb +3 -0
- metadata +54 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 0bea1df0fa0ebd3cdf2f49f5c2773357810a8dc6
|
|
4
|
+
data.tar.gz: d2153f6d8ff024cce3bdbcc71df74db5acb99d2f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f9aec4a4adccee5eba5488e7947323a51ac97e7c18395ff02d2a1cd0ac8bd7b7ae6d1fee25e8fe2f37fbd1af892494d20b4aad62037a5ec8fea5530ad65d37cf
|
|
7
|
+
data.tar.gz: 2f3fb76e833265d4a63e194fb710811f7f334748e50026916f23c71cff36a015cdea4b153542883d0bc2e47e0ee7a79c26c21143cc2354f778d5df0b9f714670
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
class ActiveRecordArchiver
|
|
2
|
+
def self.has_attribute? model, attribute
|
|
3
|
+
model.columns_hash.with_indifferent_access.has_key? attribute
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def self.relation model, attribute
|
|
7
|
+
model.reflections.with_indifferent_access[attribute]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.relation_model model, attribute
|
|
11
|
+
relation(model, attribute).class_name.constantize
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.belongs_to? model, attribute
|
|
15
|
+
!!relation(model, attribute).try(:belongs_to?)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.column model, attribute
|
|
19
|
+
model.columns_hash.with_indifferent_access[attribute]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.column_required model, attribute
|
|
23
|
+
column(model, attribute).null == false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.relation_index record, attribute
|
|
27
|
+
relevant_records = @models_hash[relation_model(record.class, attribute)].first
|
|
28
|
+
relevant_records.present? && relevant_records.index(record.send(attribute))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.relation_id model, key, value
|
|
32
|
+
relation_model_name = relation(model, key).class_name
|
|
33
|
+
@data[relation_model_name][value][:id]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.relation_foreign_key model, key
|
|
37
|
+
relation(model, key).foreign_key
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.insertable_hash model, hash
|
|
41
|
+
ret = {}
|
|
42
|
+
hash.each_pair do |key, value|
|
|
43
|
+
if column(model, key)
|
|
44
|
+
ret[column(model, key)] = value
|
|
45
|
+
elsif belongs_to?(model, key)
|
|
46
|
+
foreign_key = relation_foreign_key(model, key)
|
|
47
|
+
if !hash.include?(foreign_key) and column_required(model, foreign_key)
|
|
48
|
+
# if the foreign key is required connect it to the first available record temporarily
|
|
49
|
+
ret[column(model, foreign_key)] = relation_model(model, key).first.id
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
raise "#{key} is not an attribute or belongs_to relation of #{model}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
ret
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.relations_update_hash(model, record)
|
|
59
|
+
ret = {}
|
|
60
|
+
record.each_pair do |key, value|
|
|
61
|
+
if belongs_to?(model, key)
|
|
62
|
+
ret[relation_foreign_key(model, key)] = relation_id(model, key, value)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
ret.presence
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.assert_instance_of instance, klass
|
|
69
|
+
unless instance.class <= klass
|
|
70
|
+
raise "Object #{instance} is not an instance of the #{klass} model"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.assert_model model
|
|
75
|
+
unless model < ActiveRecord::Base
|
|
76
|
+
raise "#{model} is not an activerecord model"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
|
|
3
|
+
method ActiveRecordArchiver::export
|
|
4
|
+
|
|
5
|
+
Arguments:
|
|
6
|
+
A hash with ActiveRecord Model classes as keys and Array pairs for values.
|
|
7
|
+
The first element of each value is an iterable of ActiveRecord Record instances and
|
|
8
|
+
the second element of each value is an iterable of attribute keys and belongs_to relations.
|
|
9
|
+
Whenever a belongs relation is specified ActiveRecordArchiver will check whether the related
|
|
10
|
+
record is also included in this export and raise an Exception if it is not.
|
|
11
|
+
If it is included then it will be specified in the output as an index into the relevant array.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
A string of serialized JSON that contains the specified data including relations
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
# model definition
|
|
18
|
+
class Node < ActiveRecord::Base
|
|
19
|
+
# has attributes :name, and :next_id
|
|
20
|
+
belongs_to :next, :class_name => 'Node'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# create a cycle of nodes
|
|
24
|
+
nodes = [ Node.create(:name => 'a'),
|
|
25
|
+
Node.create(:name => 'b'),
|
|
26
|
+
Node.create(:name => 'c') ]
|
|
27
|
+
nodes[0].update_attribute :next_id, nodes[1].id
|
|
28
|
+
nodes[1].update_attribute :next_id, nodes[2].id
|
|
29
|
+
nodes[2].update_attribute :next_id, nodes[0].id
|
|
30
|
+
|
|
31
|
+
# export
|
|
32
|
+
json = ActiveRecordArchiver.export Node => [nodes, [:name, :next]]
|
|
33
|
+
|
|
34
|
+
# json is '{"Node":[{"name":"a","next":1},{"name":"b","next":2},{"name":"c","next":0}]}'
|
|
35
|
+
|
|
36
|
+
=end
|
|
37
|
+
|
|
38
|
+
class ActiveRecordArchiver
|
|
39
|
+
def self.export models_hash
|
|
40
|
+
|
|
41
|
+
@models_hash = models_hash
|
|
42
|
+
|
|
43
|
+
result = {}
|
|
44
|
+
|
|
45
|
+
# serialize
|
|
46
|
+
@models_hash.each_pair do |model, pair|
|
|
47
|
+
records, attributes = pair
|
|
48
|
+
|
|
49
|
+
result[model.to_s] = []
|
|
50
|
+
records.each do |record|
|
|
51
|
+
|
|
52
|
+
assert_instance_of record, model
|
|
53
|
+
|
|
54
|
+
rec = {}
|
|
55
|
+
attributes.each do |attribute|
|
|
56
|
+
attribute, placeholder = if attribute.is_a? Array
|
|
57
|
+
then attribute
|
|
58
|
+
else [attribute, nil] end
|
|
59
|
+
|
|
60
|
+
if has_attribute? model, attribute
|
|
61
|
+
# store attribute
|
|
62
|
+
rec[attribute] = if placeholder.nil?
|
|
63
|
+
then record.send attribute
|
|
64
|
+
else placeholder end
|
|
65
|
+
elsif belongs_to?(model, attribute)
|
|
66
|
+
# store relation
|
|
67
|
+
if (index = relation_index(record, attribute))
|
|
68
|
+
rec[attribute] = index
|
|
69
|
+
if placeholder
|
|
70
|
+
rec[relation_foreign_key(model, attribute)] = placeholder
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
raise "#{record} belongs_to #{attribute} which is not included in the export"
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
raise "#{attribute} is not an attribute or belongs_to relation of #{model}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
result[model.to_s] << rec
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# encode
|
|
84
|
+
JSON.dump result
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
|
|
3
|
+
method ActiveRecordArchiver::import
|
|
4
|
+
|
|
5
|
+
Arguments:
|
|
6
|
+
A string of JSON generated by ActiveRecordArchiver::export
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
true on success, raises an Exception and rolls back the db on failure
|
|
10
|
+
|
|
11
|
+
Effects:
|
|
12
|
+
Attempts to create and save a new record for each item in the input JSON structure and
|
|
13
|
+
connects the specified belongs_to relations
|
|
14
|
+
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
class ActiveRecordArchiver
|
|
18
|
+
def self.import json
|
|
19
|
+
|
|
20
|
+
@data = JSON.parse json
|
|
21
|
+
|
|
22
|
+
ActiveRecord::Base.transaction do
|
|
23
|
+
|
|
24
|
+
# insert records
|
|
25
|
+
@data.each_pair do |model_name, records|
|
|
26
|
+
model = model_name.constantize
|
|
27
|
+
|
|
28
|
+
assert_model model
|
|
29
|
+
|
|
30
|
+
records.each do |record|
|
|
31
|
+
record[:id] = model.all.insert(insertable_hash(model, record))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# add relations
|
|
36
|
+
@data.each_pair do |model_name, records|
|
|
37
|
+
model = model_name.constantize
|
|
38
|
+
|
|
39
|
+
records.each do |record|
|
|
40
|
+
if (update_hash = relations_update_hash(model, record))
|
|
41
|
+
instance = model.find(record[:id])
|
|
42
|
+
instance.update update_hash
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: activerecord-archiver
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sam Auciello
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-02-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |
|
|
14
|
+
ActiveRecord-Archiver is a simple tool for taking a subset of the records in one environment
|
|
15
|
+
and exporting them for use in another environment.
|
|
16
|
+
|
|
17
|
+
Design Constraints:
|
|
18
|
+
- leave out ids so as not to create collisions in the new environment
|
|
19
|
+
- preserve relations between records
|
|
20
|
+
- Allow for cyclic relationships
|
|
21
|
+
email: sam@greenriver.com
|
|
22
|
+
executables: []
|
|
23
|
+
extensions: []
|
|
24
|
+
extra_rdoc_files: []
|
|
25
|
+
files:
|
|
26
|
+
- lib/activerecord-archiver.rb
|
|
27
|
+
- lib/activerecord-archiver/archiver.rb
|
|
28
|
+
- lib/activerecord-archiver/export.rb
|
|
29
|
+
- lib/activerecord-archiver/import.rb
|
|
30
|
+
homepage: https://github.com/greenriver/activerecord-archiver
|
|
31
|
+
licenses:
|
|
32
|
+
- MIT
|
|
33
|
+
metadata: {}
|
|
34
|
+
post_install_message:
|
|
35
|
+
rdoc_options: []
|
|
36
|
+
require_paths:
|
|
37
|
+
- lib
|
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - '>='
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '0'
|
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - '>='
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
requirements: []
|
|
49
|
+
rubyforge_project:
|
|
50
|
+
rubygems_version: 2.1.11
|
|
51
|
+
signing_key:
|
|
52
|
+
specification_version: 4
|
|
53
|
+
summary: A simle tool for exporting/importing subsets of activerecord tables as JSON.
|
|
54
|
+
test_files: []
|