has_versions 0.4.9 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +7 -24
- data/has_versions.gemspec +5 -6
- data/lib/has_versions/attributes.rb +3 -3
- data/lib/has_versions/configuration.rb +0 -1
- data/lib/has_versions/reset.rb +10 -4
- data/lib/has_versions/version_methods/diff.rb +40 -0
- data/lib/has_versions/version_methods/log.rb +45 -0
- data/lib/has_versions/versioned.rb +9 -12
- data/lib/has_versions.rb +8 -9
- data/test/has_versions/attributes_test.rb +15 -10
- data/test/has_versions/configuration_test.rb +12 -0
- data/test/has_versions/reset_test.rb +29 -0
- data/test/has_versions/version_methods/diff_test.rb +68 -0
- data/test/has_versions/version_methods/log_test.rb +33 -0
- data/test/has_versions/versioned_test.rb +27 -0
- data/test/helper.rb +6 -0
- data/test/support/test_model.rb +21 -0
- data/test/support/test_version.rb +15 -0
- metadata +24 -96
- data/lib/has_versions/apply/simple.rb +0 -34
- data/lib/has_versions/apply.rb +0 -9
- data/lib/has_versions/diff/simple.rb +0 -27
- data/lib/has_versions/diff.rb +0 -7
- data/lib/has_versions/merge/always_conflicted.rb +0 -9
- data/lib/has_versions/merge/base.rb +0 -22
- data/lib/has_versions/merge/choose_first.rb +0 -9
- data/lib/has_versions/merge/diff3.rb +0 -66
- data/lib/has_versions/merge/fast_forward.rb +0 -34
- data/lib/has_versions/merge/merge_base.rb +0 -75
- data/lib/has_versions/merge/octopus.rb +0 -42
- data/lib/has_versions/merge/three_way.rb +0 -53
- data/lib/has_versions/merge.rb +0 -32
- data/lib/has_versions/reset/simple.rb +0 -19
- data/spec/has_versions/apply_spec.rb +0 -33
- data/spec/has_versions/configuration_spec.rb +0 -33
- data/spec/has_versions/diff_spec.rb +0 -40
- data/spec/has_versions/merge/diff3_spec.rb +0 -29
- data/spec/has_versions/merge/merge_base_spec.rb +0 -61
- data/spec/has_versions/merge/octopus_spec.rb +0 -74
- data/spec/has_versions/merge/three_way_spec.rb +0 -57
- data/spec/has_versions/reset_spec.rb +0 -40
- data/spec/has_versions/stage_spec.rb +0 -17
- data/spec/has_versions/versioned_spec.rb +0 -60
- data/spec/spec_helper.rb +0 -17
- data/spec/support/matchers/be_a_uuid.rb +0 -7
- data/spec/support/version.rb +0 -17
- data/test/test_helper.rb +0 -3
data/Rakefile
CHANGED
@@ -1,29 +1,12 @@
|
|
1
1
|
require 'bundler/setup'
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
3
|
|
4
|
-
require '
|
5
|
-
require 'rspec/core/rake_task'
|
6
|
-
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
|
-
spec.pattern = FileList['spec/**/*_spec.rb']
|
8
|
-
end
|
9
|
-
|
10
|
-
# require 'cucumber/rake/task'
|
11
|
-
# Cucumber::Rake::Task.new(:features)
|
12
|
-
|
13
|
-
# require 'reek/rake/task'
|
14
|
-
# Reek::Rake::Task.new do |t|
|
15
|
-
# t.fail_on_error = true
|
16
|
-
# t.verbose = false
|
17
|
-
# t.source_files = 'lib/**/*.rb'
|
18
|
-
# end
|
4
|
+
require 'rake/testtask'
|
19
5
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
task :default => :spec
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << 'lib'
|
8
|
+
t.libs << 'test'
|
9
|
+
t.pattern = 'test/**/*_test.rb'
|
10
|
+
t.verbose = false
|
11
|
+
end
|
27
12
|
|
28
|
-
# require 'yard'
|
29
|
-
# YARD::Rake::YardocTask.new
|
data/has_versions.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = "has_versions"
|
4
|
-
s.version = '0.
|
4
|
+
s.version = '0.5.0'
|
5
5
|
|
6
6
|
s.platform = Gem::Platform::RUBY
|
7
7
|
s.authors = ["Grant Rodgers"]
|
@@ -23,9 +23,8 @@ Gem::Specification.new do |s|
|
|
23
23
|
]
|
24
24
|
s.licenses = ["MIT"]
|
25
25
|
|
26
|
-
s.add_runtime_dependency("i18n", [">= 0"])
|
27
|
-
s.add_runtime_dependency("activesupport",
|
28
|
-
s.
|
29
|
-
s.
|
30
|
-
s.add_development_dependency("yard", ["~> 0.6.0"])
|
26
|
+
# s.add_runtime_dependency("i18n", [">= 0"])
|
27
|
+
s.add_runtime_dependency("activesupport", "~> 3.2")
|
28
|
+
s.add_development_dependency("rake")
|
29
|
+
# s.add_runtime_dependency("simple_uuid", [">= 0"])
|
31
30
|
end
|
@@ -3,14 +3,14 @@ module HasVersions
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
-
def
|
6
|
+
def versioned_attributes
|
7
7
|
versioning_configuration.attributes
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
def versioned_attributes
|
12
|
-
self.class.
|
13
|
-
memo[attribute] = versioning_encode_value(attribute,
|
12
|
+
self.class.versioned_attributes.inject({}) do |memo, attribute|
|
13
|
+
memo[attribute] = versioning_encode_value(attribute, send(attribute))
|
14
14
|
memo
|
15
15
|
end
|
16
16
|
end
|
data/lib/has_versions/reset.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
1
|
module HasVersions
|
2
2
|
module Reset
|
3
|
-
extend ActiveSupport::
|
3
|
+
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
module FromVersion
|
5
|
+
module ClassMethods
|
8
6
|
def from_version(version)
|
9
7
|
new.tap do |object|
|
10
8
|
object.reset!(version)
|
11
9
|
end
|
12
10
|
end
|
13
11
|
end
|
12
|
+
|
13
|
+
def reset!(version)
|
14
|
+
self.class.versioned_attributes.each do |attribute|
|
15
|
+
value = versioning_decode_value(attribute, version.snapshot[attribute])
|
16
|
+
send("#{attribute}=", value)
|
17
|
+
end
|
18
|
+
self
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module HasVersions
|
2
|
+
module VersionMethods
|
3
|
+
module Diff
|
4
|
+
def diff(original = parent)
|
5
|
+
original_snapshot = original.nil? ? {} : original.decoded_snapshot
|
6
|
+
|
7
|
+
output = {}
|
8
|
+
decoded_snapshot.each do |attribute, new_value|
|
9
|
+
original_value = original_snapshot[attribute]
|
10
|
+
|
11
|
+
if original_value != new_value
|
12
|
+
output[attribute] = [original_value, new_value]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
output
|
17
|
+
end
|
18
|
+
|
19
|
+
def modified_attributes
|
20
|
+
diff.keys
|
21
|
+
end
|
22
|
+
|
23
|
+
def different_attributes
|
24
|
+
modified_attributes.select do |attribute|
|
25
|
+
target.send(attribute) != decoded_snapshot[attribute]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def decoded_snapshot
|
30
|
+
@decoded_snapshot ||= begin
|
31
|
+
decoded = {}
|
32
|
+
target.class.versioned_attributes.each do |attribute|
|
33
|
+
decoded[attribute] = target.versioning_decode_value(attribute, snapshot[attribute])
|
34
|
+
end
|
35
|
+
decoded
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module HasVersions
|
2
|
+
module VersionMethods
|
3
|
+
class LogCollection
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(version)
|
7
|
+
@version = version
|
8
|
+
end
|
9
|
+
|
10
|
+
def walk
|
11
|
+
walker = Walker.new(@version)
|
12
|
+
while v = walker.next
|
13
|
+
yield v
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :each, :walk
|
17
|
+
|
18
|
+
def all
|
19
|
+
all = []
|
20
|
+
walk { |v| all << v }
|
21
|
+
all
|
22
|
+
end
|
23
|
+
alias_method :to_a, :all
|
24
|
+
|
25
|
+
class Walker
|
26
|
+
attr_accessor :current_version
|
27
|
+
def initialize(version)
|
28
|
+
self.current_version = version
|
29
|
+
end
|
30
|
+
|
31
|
+
def next
|
32
|
+
version = self.current_version
|
33
|
+
self.current_version = version.try(:parent)
|
34
|
+
version
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module Log
|
40
|
+
def log
|
41
|
+
@log ||= LogCollection.new(self)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -7,33 +7,30 @@ module HasVersions
|
|
7
7
|
class_attribute :version_class
|
8
8
|
|
9
9
|
include HasVersions::Attributes
|
10
|
-
include HasVersions::Reset
|
11
|
-
include HasVersions::Diff::Simple
|
12
|
-
extend HasVersions::Reset::FromVersion
|
10
|
+
include HasVersions::Reset
|
13
11
|
end
|
14
12
|
|
15
13
|
module ClassMethods
|
16
14
|
def has_versions(version_class, &block)
|
17
15
|
self.versioning_configuration = HasVersions::Configuration.new(&block)
|
18
16
|
self.version_class = version_class
|
17
|
+
version_class.class_eval do
|
18
|
+
include HasVersions::VersionMethods::Diff
|
19
|
+
include HasVersions::VersionMethods::Log
|
20
|
+
end
|
19
21
|
end
|
20
22
|
|
21
23
|
def versioned_attribute(name)
|
22
24
|
self.versioning_configuration ||= HasVersions::Configuration.new
|
23
25
|
versioning_configuration.attribute(name)
|
24
26
|
end
|
25
|
-
|
26
|
-
def is_versioned?
|
27
|
-
!versioning_configuration.blank?
|
28
|
-
end
|
29
27
|
end
|
30
28
|
|
31
|
-
# generates a version from the current object
|
32
29
|
def to_version
|
33
|
-
version_class.new
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
version = version_class.new
|
31
|
+
version.snapshot = versioned_attributes
|
32
|
+
version.target = self
|
33
|
+
version
|
37
34
|
end
|
38
35
|
end
|
39
36
|
end
|
data/lib/has_versions.rb
CHANGED
@@ -3,16 +3,10 @@ require 'active_support/all'
|
|
3
3
|
module HasVersions
|
4
4
|
extend ActiveSupport::Autoload
|
5
5
|
|
6
|
-
autoload :Configuration
|
7
|
-
autoload :Versioned
|
8
6
|
autoload :Attributes
|
9
|
-
|
10
|
-
autoload :Stage
|
11
|
-
|
12
|
-
autoload :Apply
|
13
|
-
autoload :Diff
|
14
|
-
autoload :Merge
|
7
|
+
autoload :Configuration
|
15
8
|
autoload :Reset
|
9
|
+
autoload :Versioned
|
16
10
|
|
17
11
|
module Orm
|
18
12
|
extend ActiveSupport::Autoload
|
@@ -20,7 +14,12 @@ module HasVersions
|
|
20
14
|
autoload :CassandraObject
|
21
15
|
end
|
22
16
|
|
23
|
-
|
17
|
+
module VersionMethods
|
18
|
+
extend ActiveSupport::Autoload
|
19
|
+
|
20
|
+
autoload :Diff
|
21
|
+
autoload :Log
|
22
|
+
end
|
24
23
|
end
|
25
24
|
|
26
25
|
ActiveSupport.on_load :cassandra_object do
|
@@ -1,20 +1,25 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
class HasVersions::AttributesTest < MiniTest::Spec
|
4
|
-
class VersionedObject
|
5
|
-
include HasVersions::Versioned
|
1
|
+
require 'helper'
|
6
2
|
|
3
|
+
class HasVersions::AttributesTest < MiniTest::Unit::TestCase
|
4
|
+
class VersionedModel < TestModel
|
7
5
|
attr_accessor :name
|
8
|
-
attr_accessor :
|
6
|
+
attr_accessor :weight
|
9
7
|
attr_accessor :color
|
10
8
|
|
11
9
|
has_versions(Class.new) do
|
12
10
|
attribute :name
|
13
|
-
attribute :
|
11
|
+
attribute :weight
|
14
12
|
end
|
15
13
|
end
|
16
14
|
|
17
|
-
def
|
18
|
-
assert_equal ['name', '
|
15
|
+
def test_class_versioned_attributes
|
16
|
+
assert_equal ['name', 'weight'], VersionedModel.versioned_attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_instance_versioned_attributes
|
20
|
+
record = VersionedModel.new(name: 'joe', weight: 142.0, color: 'green')
|
21
|
+
expected = {'name' => 'joe', 'weight' => '142.0'}
|
22
|
+
|
23
|
+
assert_equal expected, record.versioned_attributes
|
19
24
|
end
|
20
|
-
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HasVersions::ConfigurationTest < MiniTest::Unit::TestCase
|
4
|
+
def test_configuration
|
5
|
+
configuration = HasVersions::Configuration.new do
|
6
|
+
attribute :foo
|
7
|
+
attribute :bar
|
8
|
+
end
|
9
|
+
|
10
|
+
assert_equal %w(foo bar), configuration.attributes
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HasVersions::ResetTest < MiniTest::Unit::TestCase
|
4
|
+
class ResetModel < TestModel
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :color
|
7
|
+
|
8
|
+
has_versions(TestVersion) do
|
9
|
+
attribute :name
|
10
|
+
end
|
11
|
+
|
12
|
+
def versioning_decode_value(name, value)
|
13
|
+
"hello, #{value}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_from_version
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_reset
|
21
|
+
reset_model = ResetModel.new(name: "bob", color: "green")
|
22
|
+
version = TestVersion.new(snapshot: {name: "joe", color: "blue"})
|
23
|
+
|
24
|
+
reset_model.reset!(version)
|
25
|
+
|
26
|
+
assert_equal "hello, joe", reset_model.name
|
27
|
+
assert_equal "green", reset_model.color
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HasVersions::VersionMethods::DiffTest < MiniTest::Unit::TestCase
|
4
|
+
class DiffModel < TestModel
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :color
|
7
|
+
attr_accessor :weight
|
8
|
+
|
9
|
+
has_versions(TestVersion) do
|
10
|
+
attribute :name
|
11
|
+
attribute :weight
|
12
|
+
end
|
13
|
+
|
14
|
+
def versioning_decode_value(name, value)
|
15
|
+
"decoded: #{value}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_diff_with_parent
|
20
|
+
target = DiffModel.new
|
21
|
+
parent = TestVersion.new(target: target, snapshot: {name: 'foo', color: 'green', weight: '4.3'})
|
22
|
+
child = TestVersion.new(target: target, parent: parent, snapshot: {name: 'bar', color: 'orange', weight: '4.3'})
|
23
|
+
|
24
|
+
diff = child.diff
|
25
|
+
|
26
|
+
assert_equal({'name' => ['decoded: foo', 'decoded: bar']}, diff)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_diff_with_parameter
|
30
|
+
target = DiffModel.new
|
31
|
+
this_version = TestVersion.new(target: target, snapshot: {name: 'foo', color: 'green', weight: '4.3'})
|
32
|
+
other_version = TestVersion.new(target: target, snapshot: {name: 'bar', color: 'orange', weight: '4.3'})
|
33
|
+
|
34
|
+
diff = this_version.diff(other_version)
|
35
|
+
|
36
|
+
assert_equal({'name' => ['decoded: bar', 'decoded: foo']}, diff)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_diff_with_nil
|
40
|
+
target = DiffModel.new
|
41
|
+
version = TestVersion.new(target: target, snapshot: {name: 'bar', color: 'orange', weight: '4.3'})
|
42
|
+
|
43
|
+
diff = version.diff
|
44
|
+
|
45
|
+
assert_equal({'name' => [nil, 'decoded: bar'], 'weight' => [nil, 'decoded: 4.3']}, diff)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_modified_attributes
|
49
|
+
target = DiffModel.new
|
50
|
+
version = TestVersion.new(target: target, snapshot: {'name' => 'joe', 'weight' => '129'})
|
51
|
+
|
52
|
+
assert_equal version.snapshot.keys, version.modified_attributes
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_different_attributes
|
56
|
+
target = DiffModel.new(name: 'decoded: sam', color: 'decoded: green', weight: 'decoded: 129')
|
57
|
+
version = TestVersion.new(target: target, snapshot: {'name' => 'joe', 'weight' => '129'})
|
58
|
+
|
59
|
+
assert_equal ['name'], version.different_attributes
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_decoded_snapshot
|
63
|
+
target = DiffModel.new
|
64
|
+
version = TestVersion.new(target: target, snapshot: {name: 'foo', color: 'green', weight: '4.3'})
|
65
|
+
|
66
|
+
assert_equal({"name" => "decoded: foo", "weight" => "decoded: 4.3"}, version.decoded_snapshot)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HasVersions::VersionMethods::LogTest < MiniTest::Unit::TestCase
|
4
|
+
class LogVersion < TestVersion
|
5
|
+
include HasVersions::VersionMethods::Log
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_walk
|
9
|
+
grandparent = LogVersion.new
|
10
|
+
parent = LogVersion.new parent: grandparent
|
11
|
+
child = LogVersion.new parent: parent
|
12
|
+
|
13
|
+
ancestors = []
|
14
|
+
child.log.walk do |v|
|
15
|
+
ancestors << v
|
16
|
+
end
|
17
|
+
|
18
|
+
assert_equal [child, parent, grandparent], ancestors
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_all
|
22
|
+
grandparent = LogVersion.new
|
23
|
+
parent = LogVersion.new parent: grandparent
|
24
|
+
child = LogVersion.new parent: parent
|
25
|
+
|
26
|
+
assert_equal [child, parent, grandparent], child.log.all
|
27
|
+
assert_equal [child, parent, grandparent], child.log.to_a
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_is_enumerable
|
31
|
+
assert LogVersion.new.log.respond_to?(:collect)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HasVersions::VersionedTest < MiniTest::Unit::TestCase
|
4
|
+
def test_versioned_attribute
|
5
|
+
versioned_model = Class.new(TestModel) do
|
6
|
+
versioned_attribute 'hello'
|
7
|
+
end
|
8
|
+
|
9
|
+
assert_equal ['hello'], versioned_model.versioned_attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_to_version
|
13
|
+
record = Class.new(TestModel) do
|
14
|
+
attr_accessor :name
|
15
|
+
|
16
|
+
has_versions(TestVersion) do
|
17
|
+
attribute :name
|
18
|
+
end
|
19
|
+
end.new(name: 'wee')
|
20
|
+
|
21
|
+
version = record.to_version
|
22
|
+
|
23
|
+
assert_kind_of TestVersion, version
|
24
|
+
assert_equal record, version.target
|
25
|
+
assert_equal record.versioned_attributes, version.snapshot
|
26
|
+
end
|
27
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
class TestModel
|
2
|
+
include HasVersions::Versioned
|
3
|
+
|
4
|
+
def initialize(attributes = {})
|
5
|
+
attributes.each do |key, value|
|
6
|
+
send("#{key}=", value)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def versioning_encode_value(name, value)
|
11
|
+
value.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def versioning_decode_value(name, value)
|
15
|
+
value
|
16
|
+
end
|
17
|
+
|
18
|
+
def versioning_codable?(name)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class TestVersion
|
2
|
+
attr_reader :snapshot
|
3
|
+
attr_accessor :parent
|
4
|
+
attr_accessor :target
|
5
|
+
|
6
|
+
def initialize(attributes = {})
|
7
|
+
attributes.each do |key, value|
|
8
|
+
send("#{key}=", value)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def snapshot=(hash)
|
13
|
+
@snapshot = hash.stringify_keys
|
14
|
+
end
|
15
|
+
end
|