swim 0.0.1 → 0.0.2
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.
- 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
|