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 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
@@ -0,0 +1,3 @@
1
+ require_relative 'activerecord-archiver/archiver'
2
+ require_relative 'activerecord-archiver/export'
3
+ require_relative 'activerecord-archiver/import'
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: []