has_versions 0.4.9 → 0.5.0

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.
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