activerecord-archiver 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []