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.
Files changed (47) hide show
  1. data/Rakefile +7 -24
  2. data/has_versions.gemspec +5 -6
  3. data/lib/has_versions/attributes.rb +3 -3
  4. data/lib/has_versions/configuration.rb +0 -1
  5. data/lib/has_versions/reset.rb +10 -4
  6. data/lib/has_versions/version_methods/diff.rb +40 -0
  7. data/lib/has_versions/version_methods/log.rb +45 -0
  8. data/lib/has_versions/versioned.rb +9 -12
  9. data/lib/has_versions.rb +8 -9
  10. data/test/has_versions/attributes_test.rb +15 -10
  11. data/test/has_versions/configuration_test.rb +12 -0
  12. data/test/has_versions/reset_test.rb +29 -0
  13. data/test/has_versions/version_methods/diff_test.rb +68 -0
  14. data/test/has_versions/version_methods/log_test.rb +33 -0
  15. data/test/has_versions/versioned_test.rb +27 -0
  16. data/test/helper.rb +6 -0
  17. data/test/support/test_model.rb +21 -0
  18. data/test/support/test_version.rb +15 -0
  19. metadata +24 -96
  20. data/lib/has_versions/apply/simple.rb +0 -34
  21. data/lib/has_versions/apply.rb +0 -9
  22. data/lib/has_versions/diff/simple.rb +0 -27
  23. data/lib/has_versions/diff.rb +0 -7
  24. data/lib/has_versions/merge/always_conflicted.rb +0 -9
  25. data/lib/has_versions/merge/base.rb +0 -22
  26. data/lib/has_versions/merge/choose_first.rb +0 -9
  27. data/lib/has_versions/merge/diff3.rb +0 -66
  28. data/lib/has_versions/merge/fast_forward.rb +0 -34
  29. data/lib/has_versions/merge/merge_base.rb +0 -75
  30. data/lib/has_versions/merge/octopus.rb +0 -42
  31. data/lib/has_versions/merge/three_way.rb +0 -53
  32. data/lib/has_versions/merge.rb +0 -32
  33. data/lib/has_versions/reset/simple.rb +0 -19
  34. data/spec/has_versions/apply_spec.rb +0 -33
  35. data/spec/has_versions/configuration_spec.rb +0 -33
  36. data/spec/has_versions/diff_spec.rb +0 -40
  37. data/spec/has_versions/merge/diff3_spec.rb +0 -29
  38. data/spec/has_versions/merge/merge_base_spec.rb +0 -61
  39. data/spec/has_versions/merge/octopus_spec.rb +0 -74
  40. data/spec/has_versions/merge/three_way_spec.rb +0 -57
  41. data/spec/has_versions/reset_spec.rb +0 -40
  42. data/spec/has_versions/stage_spec.rb +0 -17
  43. data/spec/has_versions/versioned_spec.rb +0 -60
  44. data/spec/spec_helper.rb +0 -17
  45. data/spec/support/matchers/be_a_uuid.rb +0 -7
  46. data/spec/support/version.rb +0 -17
  47. data/test/test_helper.rb +0 -3
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_versions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.9
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,24 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-16 00:00:00.000000000 Z
12
+ date: 2012-06-22 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: i18n
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
30
14
  - !ruby/object:Gem::Dependency
31
15
  name: activesupport
32
16
  requirement: !ruby/object:Gem::Requirement
@@ -34,7 +18,7 @@ dependencies:
34
18
  requirements:
35
19
  - - ~>
36
20
  - !ruby/object:Gem::Version
37
- version: '3.0'
21
+ version: '3.2'
38
22
  type: :runtime
39
23
  prerelease: false
40
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,16 +26,16 @@ dependencies:
42
26
  requirements:
43
27
  - - ~>
44
28
  - !ruby/object:Gem::Version
45
- version: '3.0'
29
+ version: '3.2'
46
30
  - !ruby/object:Gem::Dependency
47
- name: simple_uuid
31
+ name: rake
48
32
  requirement: !ruby/object:Gem::Requirement
49
33
  none: false
50
34
  requirements:
51
35
  - - ! '>='
52
36
  - !ruby/object:Gem::Version
53
37
  version: '0'
54
- type: :runtime
38
+ type: :development
55
39
  prerelease: false
56
40
  version_requirements: !ruby/object:Gem::Requirement
57
41
  none: false
@@ -59,38 +43,6 @@ dependencies:
59
43
  - - ! '>='
60
44
  - !ruby/object:Gem::Version
61
45
  version: '0'
62
- - !ruby/object:Gem::Dependency
63
- name: rspec
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ~>
68
- - !ruby/object:Gem::Version
69
- version: '2.0'
70
- type: :development
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: '2.0'
78
- - !ruby/object:Gem::Dependency
79
- name: yard
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ~>
84
- - !ruby/object:Gem::Version
85
- version: 0.6.0
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ~>
92
- - !ruby/object:Gem::Version
93
- version: 0.6.0
94
46
  description: ActiveModel versioning
95
47
  email:
96
48
  - grantr@gmail.com
@@ -113,40 +65,22 @@ files:
113
65
  - features/support/env.rb
114
66
  - has_versions.gemspec
115
67
  - lib/has_versions.rb
116
- - lib/has_versions/apply.rb
117
- - lib/has_versions/apply/simple.rb
118
68
  - lib/has_versions/attributes.rb
119
69
  - lib/has_versions/configuration.rb
120
- - lib/has_versions/diff.rb
121
- - lib/has_versions/diff/simple.rb
122
- - lib/has_versions/merge.rb
123
- - lib/has_versions/merge/always_conflicted.rb
124
- - lib/has_versions/merge/base.rb
125
- - lib/has_versions/merge/choose_first.rb
126
- - lib/has_versions/merge/diff3.rb
127
- - lib/has_versions/merge/fast_forward.rb
128
- - lib/has_versions/merge/merge_base.rb
129
- - lib/has_versions/merge/octopus.rb
130
- - lib/has_versions/merge/three_way.rb
131
70
  - lib/has_versions/orm/cassandra_object.rb
132
71
  - lib/has_versions/reset.rb
133
- - lib/has_versions/reset/simple.rb
72
+ - lib/has_versions/version_methods/diff.rb
73
+ - lib/has_versions/version_methods/log.rb
134
74
  - lib/has_versions/versioned.rb
135
- - spec/has_versions/apply_spec.rb
136
- - spec/has_versions/configuration_spec.rb
137
- - spec/has_versions/diff_spec.rb
138
- - spec/has_versions/merge/diff3_spec.rb
139
- - spec/has_versions/merge/merge_base_spec.rb
140
- - spec/has_versions/merge/octopus_spec.rb
141
- - spec/has_versions/merge/three_way_spec.rb
142
- - spec/has_versions/reset_spec.rb
143
- - spec/has_versions/stage_spec.rb
144
- - spec/has_versions/versioned_spec.rb
145
- - spec/spec_helper.rb
146
- - spec/support/matchers/be_a_uuid.rb
147
- - spec/support/version.rb
148
75
  - test/has_versions/attributes_test.rb
149
- - test/test_helper.rb
76
+ - test/has_versions/configuration_test.rb
77
+ - test/has_versions/reset_test.rb
78
+ - test/has_versions/version_methods/diff_test.rb
79
+ - test/has_versions/version_methods/log_test.rb
80
+ - test/has_versions/versioned_test.rb
81
+ - test/helper.rb
82
+ - test/support/test_model.rb
83
+ - test/support/test_version.rb
150
84
  homepage: ''
151
85
  licenses:
152
86
  - MIT
@@ -176,18 +110,12 @@ test_files:
176
110
  - features/has_versions.feature
177
111
  - features/step_definitions/has_versions_steps.rb
178
112
  - features/support/env.rb
179
- - spec/has_versions/apply_spec.rb
180
- - spec/has_versions/configuration_spec.rb
181
- - spec/has_versions/diff_spec.rb
182
- - spec/has_versions/merge/diff3_spec.rb
183
- - spec/has_versions/merge/merge_base_spec.rb
184
- - spec/has_versions/merge/octopus_spec.rb
185
- - spec/has_versions/merge/three_way_spec.rb
186
- - spec/has_versions/reset_spec.rb
187
- - spec/has_versions/stage_spec.rb
188
- - spec/has_versions/versioned_spec.rb
189
- - spec/spec_helper.rb
190
- - spec/support/matchers/be_a_uuid.rb
191
- - spec/support/version.rb
192
113
  - test/has_versions/attributes_test.rb
193
- - test/test_helper.rb
114
+ - test/has_versions/configuration_test.rb
115
+ - test/has_versions/reset_test.rb
116
+ - test/has_versions/version_methods/diff_test.rb
117
+ - test/has_versions/version_methods/log_test.rb
118
+ - test/has_versions/versioned_test.rb
119
+ - test/helper.rb
120
+ - test/support/test_model.rb
121
+ - test/support/test_version.rb
@@ -1,34 +0,0 @@
1
- module HasVersions
2
- module Apply
3
-
4
- # takes a list of diff (patch) hashes and returns a copy of self with the patches applied in order.
5
- # raises ApplyFailed if the patch does not apply
6
- # diff format is:
7
- # {
8
- # attribute_name: [a, b]
9
- # }
10
- #
11
- #TODO should this be allowed to change the schema?
12
- # it will if the hash returns nil for unset keys
13
-
14
- module Simple
15
- def apply(*patches)
16
- self.class.new.tap do |version| #TODO is new the right thing here? it doe scall initialize (allocate does not)
17
- ours = snapshot.dup #TEST is it necessary to deep dup the snapshot?
18
-
19
- patches.each do |patch|
20
- patch.each do |key, (a, b)|
21
- if ours[key] == a
22
- ours[key] = b #TEST is it necessary to dup the value?
23
- else
24
- raise ApplyFailed, "Expected #{key} to be #{a.inspect}, was #{ours[key].inspect}" #TODO should this contain more information about what failed?
25
- end
26
- end
27
- end
28
-
29
- version.snapshot = ours
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,9 +0,0 @@
1
- module HasVersions
2
- class ApplyFailed < VersioningError; end
3
-
4
- module Apply
5
- extend ActiveSupport::Autoload
6
-
7
- autoload :Simple
8
- end
9
- end
@@ -1,27 +0,0 @@
1
- module HasVersions
2
- module Diff
3
- # takes a version and returns a diff (patch) from self
4
- # version must respond to #snapshot with an attributes hash (or something that responds to [] and keys)
5
- # diff format is:
6
- # {
7
- # attribute_name: [a, b]
8
- # }
9
-
10
- module Simple
11
- def diff(other)
12
- ours = snapshot
13
- theirs = other.snapshot
14
- all_keys = (ours.keys + theirs.keys).uniq
15
-
16
- output = {}.with_indifferent_access
17
- all_keys.each do |key|
18
- next unless target.versioning_codable?(key)
19
- our_value = target.versioning_decode_value(key, ours[key])
20
- their_value = target.versioning_decode_value(key, theirs[key])
21
- output[key] = [our_value, their_value] if our_value != their_value
22
- end
23
- output
24
- end
25
- end
26
- end
27
- end
@@ -1,7 +0,0 @@
1
- module HasVersions
2
- module Diff
3
- extend ActiveSupport::Autoload
4
-
5
- autoload :Simple
6
- end
7
- end
@@ -1,9 +0,0 @@
1
- module HasVersions
2
- module Merge
3
- class AlwaysConflicted < Base
4
- def conflicted?
5
- true
6
- end
7
- end
8
- end
9
- end
@@ -1,22 +0,0 @@
1
- module HasVersions
2
- module Merge
3
- class Base
4
- attr_accessor :versions, :options
5
- attr_accessor :resolution, :conflicts
6
-
7
- def initialize(versions, options={})
8
- @versions = versions
9
- @options = options
10
- @conflicts = {}.with_indifferent_access
11
- end
12
-
13
- def merge
14
- # implemented by subclasses
15
- end
16
-
17
- def conflicted?
18
- !self.conflicts.empty?
19
- end
20
- end
21
- end
22
- end
@@ -1,9 +0,0 @@
1
- module HasVersions
2
- module Merge
3
- class ChooseFirst < Base
4
- def merge
5
- self.resolution = versions.first
6
- end
7
- end
8
- end
9
- end
@@ -1,66 +0,0 @@
1
- module HasVersions
2
- module Merge
3
- module Diff3
4
-
5
- # 3-way merge, extended to support more than one other
6
- # base ours *theirs.uniq take
7
- # A A [A] A (ours)
8
- # A B [A] B (ours)
9
- # A A [B] B (theirs)
10
- # B A [A] A (ours)
11
- # B A [A,B] conflict
12
- # B A [C] conflict
13
- #
14
- # cases where values are missing:
15
- # m A A A
16
- # m B A conflict
17
- # A m A A
18
- # A m B B
19
- # A A m A
20
- # A B m B
21
- # m m A A
22
- # m A m A
23
- #
24
- # returns an array [clean, result]
25
- # where clean is a boolean
26
- # and result is an array in the conflicts case, or a single value in the clean case
27
-
28
- def diff3(base, ours, *theirs)
29
- theirs = theirs.uniq
30
-
31
- # if there is more than one possible value, it is already a conflict
32
- if theirs.size > 1
33
- # add ours to conflicts if it is different from base
34
- if ours != base && !theirs.include?(ours)
35
- theirs << ours
36
- end
37
- return [false, theirs]
38
- end
39
-
40
- theirs = theirs.first
41
-
42
- if ours == base && base == theirs
43
- [true, ours]
44
- elsif base == theirs
45
- [true, ours]
46
- elsif ours == base
47
- [true, theirs]
48
- elsif ours == theirs
49
- [true, ours]
50
- elsif ours == :_missing
51
- [true, theirs]
52
- elsif theirs == :_missing
53
- [true, ours]
54
- else
55
- [false, [ours, theirs]]
56
- end
57
- end
58
-
59
- private
60
-
61
- def value_or_missing(snapshot, key)
62
- snapshot.keys.include?(key) ? snapshot[key] : :_missing
63
- end
64
- end
65
- end
66
- end
@@ -1,34 +0,0 @@
1
- module HasVersions
2
- module Merge
3
- class FastForward < Base
4
- # Takes exactly two versions and resolves the merge using ancestry.
5
- # One of the two versions must be an ancestor of the other.
6
- #
7
- # expects versions to define contains? which returns true if the given version
8
- # is an ancestor of the receiver
9
- # eg if v1 is an ancestor of v2, then
10
- # v2.contains?(v1) #=> true
11
- #
12
- # raises MergeFailed if anything but two versions are given, or if neither version
13
- # is an ancestor of the other.
14
-
15
- def initialize(versions, options={})
16
- super
17
- if versions.size != 2
18
- raise HasVersions::MergeFailed, "Cannot fast forward merge with #{versions.size} versions"
19
- end
20
- end
21
-
22
- def merge
23
- v1, v2 = versions
24
- if v1.contains?(v2)
25
- self.resolution = v1
26
- elsif v2.contains?(v1)
27
- self.resolution = v2
28
- else
29
- raise HasVersions::MergeFailed, "cannot fast forward if neither version is an ancestor"
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,75 +0,0 @@
1
- module HasVersions
2
- module Merge
3
- module MergeBase
4
- # Finds optimal merge bases for any number of versions
5
- # Essentially a copy of git's algorithm
6
- # Expects versions to respond to parents with an array of parent versions
7
- def merge_bases(versions)
8
- versions = versions.dup
9
- one = versions.shift
10
- twos = versions
11
-
12
- bases = determine_merge_bases(one, twos)
13
- if twos.include?(one)
14
- return bases
15
- end
16
-
17
- if bases.size == 1
18
- return bases
19
- end
20
-
21
- bases.each_with_index do |base1, i|
22
- next if base1 == bases.last
23
-
24
- bases.each_with_index do |base2, j|
25
- next if base1.nil? || base2.nil?
26
- sub_bases = determine_merge_bases(base1, [base2])
27
- sub_bases.each do |sub_base|
28
- bases[i] = nil if sub_base == base1
29
- bases[j] = nil if sub_base == base2
30
- end
31
- end
32
- end
33
-
34
- bases.compact
35
- end
36
-
37
- private
38
-
39
- def determine_merge_bases(one, twos)
40
- stale = Set.new
41
- parent1 = Set.new
42
- parent2 = Set.new
43
- result = Set.new
44
-
45
- interesting = []
46
-
47
- if twos.include?(one)
48
- return [one]
49
- end
50
-
51
- parent1 << one
52
- parent2 += twos
53
-
54
- interesting << one
55
- interesting += twos
56
-
57
- while !(interesting - stale.to_a).empty?
58
- candidate = interesting.shift
59
- if parent1.include?(candidate) && parent2.include?(candidate) && !stale.include?(candidate)
60
- result << candidate
61
- end
62
-
63
- candidate.parents.each do |parent|
64
- stale << parent if stale.include?(candidate)
65
- parent1 << parent if parent1.include?(candidate)
66
- parent2 << parent if parent2.include?(candidate)
67
- interesting << parent
68
- end
69
- end
70
-
71
- result.to_a.reject { |r| stale.include?(r) }
72
- end
73
- end
74
- end
75
- end
@@ -1,42 +0,0 @@
1
- module HasVersions
2
- module Merge
3
- # Merge >2 versions
4
- # Works by doing a 3-way merge for first 2 versions, then merging the result with
5
- # the next version for each version given.
6
- # eg for 4 versions there are three merges: v1+v2, then (v1+v2)+v3, then ((v1+v2)+v3)+v4
7
- # conflicts will be an accumulation of all conflicting values from each merge
8
- class Octopus < Base
9
- def initialize(versions, options={})
10
- super
11
- if versions.size <= 2
12
- raise HasVersions::MergeFailed, "Cannot octopus merge with #{versions.size} versions, expect >2"
13
- end
14
- end
15
-
16
- def merge
17
- self.resolution = versions.inject(resolution) do |res, version|
18
- if res
19
- merge = ThreeWay.new([res, version], options)
20
- merge.merge
21
- add_conflicts(merge.conflicts)
22
- res = merge.resolution
23
- else
24
- res = version
25
- end
26
- res
27
- end
28
- resolution.parents = versions
29
- resolution
30
- end
31
-
32
- private
33
- def add_conflicts(conflicts)
34
- conflicts.each do |attribute, values|
35
- self.conflicts[attribute] ||= []
36
- self.conflicts[attribute] += values
37
- self.conflicts[attribute].uniq!
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,53 +0,0 @@
1
- module HasVersions
2
- module Merge
3
- # Merges two versions using ancestry and 3-way diff
4
- # expects versions to respond to parents= to set parents
5
- # does not check ancestry when setting parents - use FastForward if one version
6
- # is an ancestor of another or they are identical
7
- class ThreeWay < Base
8
- include MergeBase
9
- include Diff3
10
-
11
- def initialize(versions, options={})
12
- super
13
- if versions.size != 2
14
- raise HasVersions::MergeFailed, "Cannot 3-way merge with #{versions.size} versions, expect 2"
15
- end
16
- end
17
-
18
- # assume the first version is ours
19
- def merge
20
- version_class = options[:version_class] || versions.first.class
21
- # find merge base or bases
22
- bases = merge_bases(versions)
23
- # raise if there is more than one base
24
- # TODO could potentially just choose one (that's what git does)
25
- raise MergeFailed, "More than one merge base" if bases.size > 1
26
-
27
- base = bases.first.try(:snapshot) || {}.with_indifferent_access
28
- ours = versions.first.snapshot
29
- theirs = versions.last.snapshot
30
-
31
- self.resolution = version_class.new
32
- self.resolution.parents = versions
33
-
34
- # get the full list of attributes in each version and the merge base
35
- attributes = (versions.collect { |v| v.snapshot.keys }.flatten + base.keys).uniq
36
-
37
- # do 3-way merge on each attribute
38
- attributes.each do |key|
39
- clean, result = diff3(value_or_missing(base, key), value_or_missing(ours, key), value_or_missing(theirs, key))
40
- if clean
41
- resolution.snapshot[key] = result
42
- else
43
- resolution.snapshot[key] = ours[key]
44
- conflicts[key] = result
45
- end
46
- end
47
-
48
- resolution
49
- end
50
- end
51
- end
52
- end
53
-
@@ -1,32 +0,0 @@
1
- module HasVersions
2
-
3
- class MergeFailed < VersioningError; end
4
-
5
- class MergeConflict < MergeFailed
6
- attr_accessor :conflicts
7
- attr_accessor :result
8
-
9
- def initialize(conflicts=nil, result=nil)
10
- @conflicts = conflicts
11
- @result = result
12
- end
13
- end
14
-
15
- module Merge
16
- extend ActiveSupport::Autoload
17
-
18
- autoload :Base
19
-
20
- autoload :FastForward
21
- autoload :ThreeWay
22
- autoload :Octopus
23
-
24
- # strategies for testing
25
- autoload :ChooseFirst
26
- autoload :AlwaysConflicted
27
-
28
- autoload :Diff3
29
- autoload :MergeBase
30
-
31
- end
32
- end
@@ -1,19 +0,0 @@
1
- module HasVersions
2
- module Reset
3
- # reset should:
4
- # take a version
5
- # update self with the attributes from the version
6
- # return updated self
7
-
8
- module Simple
9
- def reset!(version)
10
- self.class.versioned_fields.each do |name|
11
- value = versioning_decode_value(name, version.snapshot[name])
12
- __send__("#{name}=", value)
13
- end
14
- self
15
- end
16
-
17
- end
18
- end
19
- end
@@ -1,33 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class ApplyingVersion < Version; end
4
-
5
- describe ApplyingVersion do
6
- class ApplyingVersion
7
- include HasVersions::Apply::Simple
8
- end
9
-
10
- let (:foo1) { described_class.new(name: 'foo1') }
11
- let (:foo2) { described_class.new(name: 'foo2') }
12
-
13
- let (:patch1) { {name: ['foo1', 'foo2']}.with_indifferent_access }
14
- let (:patch2) { {name: ['foo2', 'foo1'], stripey: [nil, 'socks']}.with_indifferent_access }
15
- let (:patch3) { {name: ['foo2', 'foo3']}.with_indifferent_access }
16
-
17
- it 'should patch the snapshot' do
18
- foo1.apply(patch1).snapshot.should include(name: 'foo2')
19
- end
20
-
21
- it 'should raise on non-applying patch' do
22
- expect { foo1.apply(patch2) }.to raise_error(HasVersions::ApplyFailed)
23
- end
24
-
25
- it 'should apply patches adding attributes', :focus => true do
26
- foo2.apply(patch2).snapshot.should include(name: 'foo1')
27
- foo2.apply(patch2).snapshot.should include(stripey: 'socks')
28
- end
29
-
30
- it 'should apply multiple patches' do
31
- foo1.apply(patch1, patch3).snapshot.should include(name: 'foo3')
32
- end
33
- end