swim 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -0
- data/.rspec +0 -0
- data/Gemfile +2 -1
- data/LICENSE +0 -0
- data/README.md +16 -2
- data/lib/swim.rb +2 -0
- data/lib/swim/change.rb +49 -27
- data/lib/swim/sync_tools.rb +29 -16
- data/lib/swim/version.rb +1 -1
- data/spec/db/development.sqlite3 +0 -0
- data/spec/lib/swim_spec.rb +0 -0
- data/spec/lib/sync_tools_spec.rb +46 -38
- data/spec/sample_files/almost_valid_class_settings.json +0 -0
- data/spec/sample_files/{artist_settings.json → artist_1_settings.json} +0 -0
- data/spec/sample_files/artist_2_settings.json +1 -0
- data/spec/sample_models/album.rb +0 -0
- data/spec/sample_models/almost_valid_class.rb +9 -0
- data/spec/sample_models/artist.rb +2 -2
- data/spec/sample_models/invalid_class.rb +8 -0
- data/spec/spec_helper.rb +0 -0
- data/swim.gemspec +2 -1
- metadata +34 -6
data/.gitignore
CHANGED
File without changes
|
data/.rspec
CHANGED
File without changes
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Swim
|
2
2
|
|
3
|
-
|
3
|
+
Swim provides tools for synchronizing a tree of ActiveRecord objects with a previously-saved JSON file.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -18,7 +18,21 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
-
|
21
|
+
1. Add a settings_file instance method to your ActiveRecord class.
|
22
|
+
|
23
|
+
class Artist < ActiveRecord::Base
|
24
|
+
def settings_file
|
25
|
+
File.expand_path("/sample_files/artist_1_settings.json")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
2. Add a sync_settings class method to your ActiveRecord class.
|
30
|
+
|
31
|
+
class Artist < ActiveRecord::Base
|
32
|
+
def self.sync_settings
|
33
|
+
{ :albums => {} }
|
34
|
+
end
|
35
|
+
end
|
22
36
|
|
23
37
|
## Contributing
|
24
38
|
|
data/lib/swim.rb
CHANGED
data/lib/swim/change.rb
CHANGED
@@ -1,37 +1,36 @@
|
|
1
|
+
# Class to store changes made during SyncTools' import_json_file method.
|
1
2
|
class Swim::Change
|
2
3
|
attr_accessor :obj_class, :obj_id, :change_type, :key, :old_value, :new_value, :status, :errors
|
3
|
-
|
4
|
-
def initialize(
|
5
|
-
obj_class
|
6
|
-
obj_id =
|
7
|
-
change_type =
|
8
|
-
key =
|
9
|
-
old_value =
|
10
|
-
new_value =
|
11
|
-
status =
|
12
|
-
errors =
|
13
|
-
end
|
14
|
-
|
15
|
-
def new(args)
|
16
|
-
end
|
17
|
-
|
18
|
-
def item
|
19
|
-
obj_class.constantize.send(:find, obj_id)
|
20
|
-
end
|
21
|
-
|
22
|
-
def update(i)
|
23
|
-
i.update_attribute(key, new_value)
|
4
|
+
|
5
|
+
def initialize(attributes = nil)
|
6
|
+
self.obj_class = attributes[:obj_class]
|
7
|
+
self.obj_id = attributes[:obj_id]
|
8
|
+
self.change_type = attributes[:change_type]
|
9
|
+
self.key = attributes[:key]
|
10
|
+
self.old_value = attributes[:old_value]
|
11
|
+
self.new_value = attributes[:new_value]
|
12
|
+
self.status = attributes[:status]
|
13
|
+
self.errors = attributes[:errors]
|
24
14
|
end
|
25
15
|
|
26
|
-
def
|
27
|
-
|
16
|
+
def to_s
|
17
|
+
if status.nil? || status.empty?
|
18
|
+
verb = "would be"
|
19
|
+
elsif status == "succeeded"
|
20
|
+
verb = "was"
|
21
|
+
elsif status == "failed"
|
22
|
+
verb = "could not be"
|
23
|
+
end
|
24
|
+
if change_type == :update
|
25
|
+
"#{obj_class}(#{obj_id})##{key} #{verb} #{old_value} (instead of #{new_value})."
|
26
|
+
else
|
27
|
+
"#{obj_class}(#{obj_id}) #{verb} #{change_type}d (#{ change_type == :insert ? new_value.to_hash : old_value.to_hash })."
|
28
|
+
end
|
28
29
|
end
|
29
30
|
|
30
|
-
def
|
31
|
-
i = obj_class.constantize.send(:new, new_value)
|
32
|
-
return i
|
31
|
+
def new(args)
|
33
32
|
end
|
34
|
-
|
33
|
+
|
35
34
|
def process
|
36
35
|
if change_type == :update
|
37
36
|
i = item
|
@@ -55,4 +54,27 @@ class Swim::Change
|
|
55
54
|
return false
|
56
55
|
end
|
57
56
|
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Convenience method for accessing the changed object in the database
|
61
|
+
def item
|
62
|
+
obj_class.constantize.send(:find, obj_id)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Method to execute the attribute update described by the Change
|
66
|
+
def update(i)
|
67
|
+
i.update_attribute(key, new_value)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Method to execute the object deletion described by the Change
|
71
|
+
def delete(i)
|
72
|
+
i.destroy
|
73
|
+
end
|
74
|
+
|
75
|
+
# Method to execute the object insertion described by the Change
|
76
|
+
def insert
|
77
|
+
i = obj_class.constantize.send(:new, new_value)
|
78
|
+
return i
|
79
|
+
end
|
58
80
|
end
|
data/lib/swim/sync_tools.rb
CHANGED
@@ -1,25 +1,31 @@
|
|
1
|
-
require 'active_support/all'
|
2
|
-
|
3
1
|
class SyncSettingsMissing < StandardError
|
4
2
|
end
|
5
3
|
|
6
|
-
class
|
4
|
+
class SettingsPathMissing < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
class ComparisonHashMissing < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class InvalidSettingsPath < StandardError
|
7
11
|
end
|
8
12
|
|
9
13
|
|
14
|
+
# Tools for syncing a tree of ActiveRecord objects using a JSON file.
|
10
15
|
class SyncTools
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
+
|
17
|
+
# Save a JSON representaton of the object to a file, specified by
|
18
|
+
def self.save_settings(obj)
|
19
|
+
str = obj.to_json(:include => obj.class.sync_settings )
|
20
|
+
File.open(File.expand_path(obj.settings_path), 'w+') {|f| f.write(str) }
|
16
21
|
end
|
17
|
-
|
22
|
+
|
23
|
+
# compares a tree of ActiveRecord objects to a previously-saved JSON file
|
18
24
|
def self.compare_json_file(obj)
|
19
|
-
unless obj.methods.include?(:
|
20
|
-
raise
|
25
|
+
unless obj.methods.include?(:settings_path) || obj.methods.include?('settings_path')
|
26
|
+
raise SettingsPathMissing, "#{obj.class.to_s} must define a class method named settings_file."
|
21
27
|
end
|
22
|
-
json = json_file(obj.
|
28
|
+
json = json_file(File.expand_path(obj.settings_path))
|
23
29
|
|
24
30
|
unless obj.class.methods.include?(:sync_settings) || obj.class.methods.include?("sync_settings")
|
25
31
|
raise SyncSettingsMissing, "#{obj.class.to_s} must define a class method named sync_settings that returns a hash."
|
@@ -32,8 +38,8 @@ class SyncTools
|
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
35
|
-
def self.import_json_file(obj,
|
36
|
-
changes = Swim::SyncTools.compare_json_file(self,
|
41
|
+
def self.import_json_file(obj, settings_path)
|
42
|
+
changes = Swim::SyncTools.compare_json_file(self, settings_path)
|
37
43
|
completed = []
|
38
44
|
not_completed = []
|
39
45
|
changes.each do |ch|
|
@@ -72,6 +78,7 @@ class SyncTools
|
|
72
78
|
value = value.utc.to_s
|
73
79
|
end
|
74
80
|
unless value == hsh_value
|
81
|
+
# p "#{obj.class.to_s} #{obj.id} #{key} #{value} != #{hsh_value}"
|
75
82
|
@changes << Swim::Change.new(:obj_class => obj.class.to_s, :obj_id => obj.id, :change_type => :update, :key => key, :old_value => value, :new_value => hsh_value)
|
76
83
|
else
|
77
84
|
# p "#{obj.class.to_s} #{key} #{value} == #{hsh_value}"
|
@@ -84,7 +91,7 @@ class SyncTools
|
|
84
91
|
if arr.length == 0 && hsh.nil?
|
85
92
|
return
|
86
93
|
elsif hsh.nil?
|
87
|
-
|
94
|
+
raise ComparisonHashMissing, "Array (#{arr.collect{|ar| ar.class.to_s }}) has no hash for comparison"
|
88
95
|
end
|
89
96
|
hsh_members = hsh.collect{ |h| h["id"] }
|
90
97
|
arr.each do |a|
|
@@ -102,5 +109,11 @@ class SyncTools
|
|
102
109
|
end
|
103
110
|
end
|
104
111
|
end
|
105
|
-
|
112
|
+
|
113
|
+
# loads and decodes a specified json_file
|
114
|
+
def self.json_file(settings_path)
|
115
|
+
file = File.open(settings_path, "rb")
|
116
|
+
contents = file.read
|
117
|
+
ActiveSupport::JSON.decode(contents)
|
118
|
+
end
|
106
119
|
end
|
data/lib/swim/version.rb
CHANGED
data/spec/db/development.sqlite3
CHANGED
File without changes
|
data/spec/lib/swim_spec.rb
CHANGED
File without changes
|
data/spec/lib/sync_tools_spec.rb
CHANGED
@@ -3,47 +3,55 @@ require 'spec_helper'
|
|
3
3
|
require 'swim/change'
|
4
4
|
require 'swim/sync_tools'
|
5
5
|
|
6
|
-
class AlmostValidClass
|
7
|
-
def settings_file
|
8
|
-
File.expand_path("#{__FILE__}/../../sample_files/almost_valid_class_settings.json")
|
9
|
-
end
|
10
|
-
|
11
|
-
# def self.sync_settings
|
12
|
-
# return Hash.new
|
13
|
-
# end
|
14
|
-
end
|
15
|
-
|
16
|
-
class InvalidClass
|
17
|
-
# def self.settings_file
|
18
|
-
# return Hash.new
|
19
|
-
# end
|
20
|
-
# def self.sync_settings
|
21
|
-
# return Hash.new
|
22
|
-
# end
|
23
|
-
end
|
24
|
-
|
25
6
|
describe SyncTools do
|
26
7
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
8
|
+
context "necessary settings" do
|
9
|
+
it "should require the object to define settings_path" do
|
10
|
+
expect { SyncTools::compare_json_file(InvalidClass.new) }.to raise_error(
|
11
|
+
SettingsPathMissing
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should require the object to define sync_settings" do
|
16
|
+
expect { SyncTools::compare_json_file(AlmostValidClass.new) }.to raise_error(
|
17
|
+
SyncSettingsMissing
|
18
|
+
)
|
19
|
+
end
|
32
20
|
end
|
33
21
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
22
|
+
context "compare_json_file" do
|
23
|
+
it "should return a change when one exists" do
|
24
|
+
artist = Artist.find(1)
|
25
|
+
changes = SyncTools::compare_json_file(artist)
|
26
|
+
changes.should be_an_instance_of(Array)
|
27
|
+
changes.length.should == 1
|
28
|
+
changes.first.should be_an_instance_of(Swim::Change)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should correctly identify changes" do
|
32
|
+
artist = Artist.find(2)
|
33
|
+
SyncTools.save_settings(artist)
|
34
|
+
artist.albums.first.title = "The Best of Billy Prestone"
|
35
|
+
changes = SyncTools::compare_json_file(artist)
|
36
|
+
changes.should be_an_instance_of(Array)
|
37
|
+
changes.length.should == 1
|
38
|
+
changes.first.should be_an_instance_of(Swim::Change)
|
39
|
+
end
|
39
40
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
|
42
|
+
context "#save_settings" do
|
43
|
+
it "should save settings in a json file" do
|
44
|
+
artist = Artist.find(2)
|
45
|
+
expect { SyncTools.save_settings(artist) }
|
46
|
+
end
|
47
|
+
|
48
|
+
it "settings and object should be identical" do
|
49
|
+
artist = Artist.find(2)
|
50
|
+
SyncTools.save_settings(artist)
|
51
|
+
changes = SyncTools::compare_json_file(artist)
|
52
|
+
changes.should be_an_instance_of(Array)
|
53
|
+
changes.length.should == 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
49
57
|
end
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
{"artist":{"created_at":"2012-07-03T14:02:14-04:00","id":2,"name":"Billy Preston","updated_at":"2012-07-03T14:02:14-04:00","albums":[{"artist_id":2,"created_at":"2012-07-03T14:02:14-04:00","id":2,"title":"The Best of Billy Preston","updated_at":"2012-07-03T14:02:14-04:00"}]}}
|
data/spec/sample_models/album.rb
CHANGED
File without changes
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class Artist < ActiveRecord::Base
|
2
2
|
has_many :albums
|
3
3
|
|
4
|
-
def
|
5
|
-
|
4
|
+
def settings_path
|
5
|
+
"#{__FILE__}/../../sample_files/artist_#{id}_settings.json"
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.sync_settings
|
data/spec/spec_helper.rb
CHANGED
File without changes
|
data/swim.gemspec
CHANGED
@@ -6,9 +6,10 @@ Gem::Specification.new do |gem|
|
|
6
6
|
gem.email = ["jerry@disruptiveventures.com"]
|
7
7
|
gem.description = %q{Sync or Swim}
|
8
8
|
gem.summary = %q{Synchronize ActiveRecord Objects Across Environments with JSON and git}
|
9
|
-
gem.homepage = ""
|
9
|
+
gem.homepage = "http://disruptive.github.com/swim"
|
10
10
|
|
11
11
|
gem.add_development_dependency 'rspec'
|
12
|
+
gem.add_development_dependency 'sqlite3'
|
12
13
|
gem.files = `git ls-files`.split($\)
|
13
14
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
15
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: swim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sqlite3
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
description: Sync or Swim
|
31
47
|
email:
|
32
48
|
- jerry@disruptiveventures.com
|
@@ -48,12 +64,15 @@ files:
|
|
48
64
|
- spec/lib/swim_spec.rb
|
49
65
|
- spec/lib/sync_tools_spec.rb
|
50
66
|
- spec/sample_files/almost_valid_class_settings.json
|
51
|
-
- spec/sample_files/
|
67
|
+
- spec/sample_files/artist_1_settings.json
|
68
|
+
- spec/sample_files/artist_2_settings.json
|
52
69
|
- spec/sample_models/album.rb
|
70
|
+
- spec/sample_models/almost_valid_class.rb
|
53
71
|
- spec/sample_models/artist.rb
|
72
|
+
- spec/sample_models/invalid_class.rb
|
54
73
|
- spec/spec_helper.rb
|
55
74
|
- swim.gemspec
|
56
|
-
homepage:
|
75
|
+
homepage: http://disruptive.github.com/swim
|
57
76
|
licenses: []
|
58
77
|
post_install_message:
|
59
78
|
rdoc_options: []
|
@@ -65,15 +84,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
84
|
- - ! '>='
|
66
85
|
- !ruby/object:Gem::Version
|
67
86
|
version: '0'
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
hash: 3293738542906190303
|
68
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
91
|
none: false
|
70
92
|
requirements:
|
71
93
|
- - ! '>='
|
72
94
|
- !ruby/object:Gem::Version
|
73
95
|
version: '0'
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
hash: 3293738542906190303
|
74
99
|
requirements: []
|
75
100
|
rubyforge_project:
|
76
|
-
rubygems_version: 1.8.
|
101
|
+
rubygems_version: 1.8.24
|
77
102
|
signing_key:
|
78
103
|
specification_version: 3
|
79
104
|
summary: Synchronize ActiveRecord Objects Across Environments with JSON and git
|
@@ -82,7 +107,10 @@ test_files:
|
|
82
107
|
- spec/lib/swim_spec.rb
|
83
108
|
- spec/lib/sync_tools_spec.rb
|
84
109
|
- spec/sample_files/almost_valid_class_settings.json
|
85
|
-
- spec/sample_files/
|
110
|
+
- spec/sample_files/artist_1_settings.json
|
111
|
+
- spec/sample_files/artist_2_settings.json
|
86
112
|
- spec/sample_models/album.rb
|
113
|
+
- spec/sample_models/almost_valid_class.rb
|
87
114
|
- spec/sample_models/artist.rb
|
115
|
+
- spec/sample_models/invalid_class.rb
|
88
116
|
- spec/spec_helper.rb
|