tfs_graph 0.1.1 → 0.1.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +12 -4
  4. data/Rakefile +1 -0
  5. data/lib/tfs_graph.rb +1 -2
  6. data/lib/tfs_graph/abstract_store.rb +23 -0
  7. data/lib/tfs_graph/associators/branch_associator.rb +1 -1
  8. data/lib/tfs_graph/associators/changeset_tree_builder.rb +29 -0
  9. data/lib/tfs_graph/behaviors.rb +9 -0
  10. data/lib/tfs_graph/behaviors/neo4j_repository/branch.rb +39 -0
  11. data/lib/tfs_graph/behaviors/neo4j_repository/changeset.rb +12 -0
  12. data/lib/tfs_graph/behaviors/neo4j_repository/project.rb +89 -0
  13. data/lib/tfs_graph/behaviors/related_repository/branch.rb +26 -0
  14. data/lib/tfs_graph/behaviors/related_repository/changeset.rb +8 -0
  15. data/lib/tfs_graph/behaviors/related_repository/project.rb +48 -0
  16. data/lib/tfs_graph/branch.rb +84 -37
  17. data/lib/tfs_graph/branch/branch_archive_handler.rb +10 -6
  18. data/lib/tfs_graph/branch/branch_store.rb +13 -26
  19. data/lib/tfs_graph/changeset.rb +46 -25
  20. data/lib/tfs_graph/changeset/changeset_normalizer.rb +1 -0
  21. data/lib/tfs_graph/changeset/changeset_store.rb +13 -36
  22. data/lib/tfs_graph/changeset_merge.rb +20 -18
  23. data/lib/tfs_graph/changeset_merge/changeset_merge_store.rb +13 -10
  24. data/lib/tfs_graph/config.rb +12 -4
  25. data/lib/tfs_graph/entity.rb +34 -6
  26. data/lib/tfs_graph/extensions.rb +27 -0
  27. data/lib/tfs_graph/graph_populator.rb +9 -1
  28. data/lib/tfs_graph/persistable_entity.rb +60 -0
  29. data/lib/tfs_graph/populators/everything.rb +16 -4
  30. data/lib/tfs_graph/populators/for_archived_branch.rb +28 -0
  31. data/lib/tfs_graph/populators/for_branch.rb +35 -0
  32. data/lib/tfs_graph/populators/for_project.rb +22 -5
  33. data/lib/tfs_graph/populators/since_date.rb +21 -5
  34. data/lib/tfs_graph/populators/since_last.rb +22 -10
  35. data/lib/tfs_graph/populators/utilities.rb +4 -19
  36. data/lib/tfs_graph/project.rb +49 -13
  37. data/lib/tfs_graph/project/project_store.rb +13 -22
  38. data/lib/tfs_graph/repository.rb +78 -0
  39. data/lib/tfs_graph/repository/neo4j_repository.rb +97 -0
  40. data/lib/tfs_graph/repository/related_repository.rb +89 -0
  41. data/lib/tfs_graph/repository_registry.rb +60 -0
  42. data/lib/tfs_graph/server_registry.rb +45 -0
  43. data/lib/tfs_graph/store_helpers.rb +4 -5
  44. data/lib/tfs_graph/version.rb +1 -1
  45. data/schema.cypher +7 -0
  46. data/spec/branch_spec.rb +120 -0
  47. data/spec/neo4j_repository_integration_spec.rb +346 -0
  48. data/spec/persistable_entity_spec.rb +91 -0
  49. data/spec/project_spec.rb +29 -0
  50. data/spec/related_repository_integration_spec.rb +328 -0
  51. data/spec/repository_registry_spec.rb +48 -0
  52. data/spec/repository_spec.rb +73 -0
  53. data/spec/server_registery_spec.rb +36 -0
  54. data/spec/spec_helper.rb +12 -24
  55. data/tfs_graph.gemspec +3 -2
  56. metadata +67 -21
  57. data/lib/tfs_graph/associators/changeset_tree_creator.rb +0 -19
  58. data/spec/factories.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 305a88a142479775b0507af876a4e432b5069c62
4
- data.tar.gz: 82b14fc50dd7d1b0fbb76bde73d861c4e6e6fef2
3
+ metadata.gz: 3024420a800c31fc3676483f1bbd658dda5c22d0
4
+ data.tar.gz: 527426b7e59e38f884db6876c264d655b11836a0
5
5
  SHA512:
6
- metadata.gz: 383e2e25094ffc75ef1ec0a8b42fb2c8d3cc5c34198a6149ed9a633d2249f0bfdac5ae36040d70690fbe677afe2d95a8ef3950093b32c6b5b0cf4fa249bf8c65
7
- data.tar.gz: 08b56fcb8b084152b282362434a4e692e19e967b5f626e767426b0cf1296d9b93431ba0fc496f1b02b9e7c55988cb6b23e67cf44360a5f160d4dd46ea875d953
6
+ metadata.gz: 43141894fdff22dd10f8a25864af7cf41875dc611320c7671cfc91a2d60c02ae4a0e2e4a11a7f13044e3f3aee5c713e6be32d1b44c71b8a8099407512380e47e
7
+ data.tar.gz: a70fad3373c4ac4bddd91e0cfe722abe255f73f90dd6eaf75e150d5cb09e08cc80a69a5fc3c92cc9e35c3f006bf92d29a9e6174219e6505e838d926450ca1455
@@ -1 +1 @@
1
- 2.0.0-p247
1
+ 2.1.0
data/Gemfile CHANGED
@@ -3,17 +3,25 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in tfs_graph.gemspec
4
4
  gemspec
5
5
 
6
- gem "related", git: "https://github.com/plukevdh/related.git", branch: "preserve_external_ids"
7
-
8
6
  group :test do
9
7
  gem 'rspec'
10
8
  gem 'rspec-given'
9
+ gem 'flexmock'
11
10
  gem 'webmock'
12
11
  gem 'vcr'
13
- gem 'factory_girl'
14
12
  end
15
13
 
16
14
  group :test, :development do
17
15
  gem "pry"
18
16
  gem 'benchmark-ips'
19
- end
17
+ gem "related", git: "https://github.com/plukevdh/related.git", branch: "namespace_fix"
18
+ gem 'timecop'
19
+
20
+ gem 'neo4j-core', git: "https://github.com/plukevdh/neo4j-core.git"
21
+
22
+ platforms :jruby do
23
+ gem 'neo4j-community'
24
+ end
25
+ end
26
+
27
+ gem 'simplecov', :require => false, :group => :test
data/Rakefile CHANGED
@@ -1 +1,2 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'neo4j/tasks/neo4j_server'
@@ -1,7 +1,6 @@
1
- require "related"
2
-
3
1
  require "tfs_graph/version"
4
2
  require "tfs_graph/config"
3
+ require 'tfs_graph/repository_registry'
5
4
  require 'tfs_graph/graph_populator'
6
5
 
7
6
  module TFSGraph
@@ -0,0 +1,23 @@
1
+ require 'tfs_graph/tfs_client'
2
+ require 'tfs_graph/tfs_helpers'
3
+ require 'tfs_graph/store_helpers'
4
+
5
+ module TFSGraph
6
+ class AbstractStore
7
+ include TFSClient
8
+ include TFSHelpers
9
+ include StoreHelpers
10
+
11
+ def fetch_and_cache
12
+ cache_all fetch_all
13
+ end
14
+
15
+ def fetch_all
16
+ normalize root_query.run
17
+ end
18
+
19
+ def cache_all(attr_set)
20
+ attr_set.map {|attrs| cache(attrs) }
21
+ end
22
+ end
23
+ end
@@ -18,7 +18,7 @@ module TFSGraph
18
18
  return if root.nil?
19
19
 
20
20
  change.parent = root.id
21
- change.save
21
+ change.save!
22
22
  end
23
23
  end
24
24
  end
@@ -0,0 +1,29 @@
1
+ module TFSGraph
2
+ class ChangesetTreeBuilder
3
+ class << self
4
+ def to_tree(branch, changesets)
5
+ changesets.map.with_index do |changeset, i|
6
+ parent = (i == 0) ? branch : changesets[i-1]
7
+
8
+ if parent.is_a? TFSGraph::Changeset
9
+ changeset.parent = parent.id
10
+ changeset.save!
11
+ end
12
+
13
+ parent.add_child changeset
14
+ changeset
15
+ end
16
+ end
17
+
18
+ def set_branch_merges(changesets)
19
+ changesets.each do |cs|
20
+ from = cs.merges.max
21
+ next unless from
22
+
23
+ cs.merge_parent = from.id
24
+ cs.save!
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ module TFSGraph
2
+ class Behaviors
3
+ end
4
+ end
5
+
6
+ types = %w{project branch changeset}
7
+
8
+ types.each {|type| require_relative "behaviors/related_repository/#{type}" }
9
+ types.each {|type| require_relative "behaviors/neo4j_repository/#{type}" }
@@ -0,0 +1,39 @@
1
+ module TFSGraph
2
+ class Behaviors
3
+ class Neo4jRepository
4
+ module Branch
5
+ def find_in_project(project, path)
6
+ # project.branches.detect {|b| b.path == path }
7
+ rebuild Neo4j::Label.query(:branch, conditions: {path: path, project: project.name }).first
8
+ end
9
+
10
+ def find_by_path(path)
11
+ branch = Neo4j::Label.query(:branch, conditions: {path: path}).first
12
+ raise TFSGraph::Repository::NotFound, "No branch found for #{path}" if branch.nil?
13
+
14
+ rebuild branch
15
+ end
16
+
17
+ def absolute_root_for(branch)
18
+ root = branch
19
+ proj = project_for_branch branch
20
+
21
+ until(root.master?) do
22
+ root = proj.branches.detect {|b| b.path == root.root }
23
+ end
24
+
25
+ root
26
+ end
27
+
28
+ def project_for_branch(branch)
29
+ RepositoryRegistry.instance.project_repository.find_by_name branch.project
30
+ end
31
+
32
+ private
33
+ def fetch_existing_record(obj)
34
+ find_by_path(obj.path).db_object
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ module TFSGraph
2
+ class Behaviors
3
+ class Neo4jRepository
4
+ module Changeset
5
+ private
6
+ def fetch_existing_record(obj)
7
+ find_native(obj.id)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,89 @@
1
+ module TFSGraph
2
+ class Behaviors
3
+ class Neo4jRepository
4
+ module Project
5
+ ROOT_BRANCH_QUERY = "MATCH (p:project {name: {project}})-[:branches]->(b:branch) WHERE (b.original_path = {path} OR b.root = {path})"
6
+ ACTIVITY_QUERY = "MATCH (a:project)-[:branches]->(b:branch)-[:changesets]->(c:changeset) where a.name = {name}"
7
+
8
+ def create(args)
9
+ obj = super
10
+ relate :projects, root, obj.db_object
11
+
12
+ obj
13
+ end
14
+
15
+ def all
16
+ get_nodes(root, :outgoing, :projects, TFSGraph::Project)
17
+ end
18
+
19
+ def active
20
+ projects = session.query "MATCH (p:project {hidden: 'false'}) RETURN p as `project`, ID(p) as `neo_id`"
21
+ rebuild_for_type self, projects, :project
22
+ end
23
+
24
+ def find_by_name(name)
25
+ project = Neo4j::Label.query(:project, conditions: {name: name}).first
26
+ raise TFSGraph::Repository::NotFound, "No project found for #{name}" if project.nil?
27
+
28
+ rebuild project
29
+ end
30
+
31
+ def root_branches(project)
32
+ roots = session.query "MATCH (p:project {name: {project}})-[:branches]->(b:branch {hidden: 'false', archived: 'false'}) where not has(b.root) RETURN b AS `branch`, ID(b) AS `neo_id`",
33
+ project: project.name
34
+
35
+ rebuild_for_type RepositoryRegistry.branch_repository, roots, :branch
36
+ end
37
+
38
+ def branches_for_root(project, branch)
39
+ branches = session.query "#{ROOT_BRANCH_QUERY} RETURN b as `branch`, ID(b) as `neo_id`",
40
+ project: project.name,
41
+ path: branch.path
42
+
43
+ rebuild_for_type RepositoryRegistry.branch_repository, branches, :branch
44
+ end
45
+
46
+ def active_branches_for_root(project, branch)
47
+ branches = session.query "#{ROOT_BRANCH_QUERY} AND b.archived = 'false' AND b.hidden = 'false' RETURN b as `branch`, ID(b) as `neo_id`",
48
+ project: project.name,
49
+ path: branch.path
50
+
51
+ rebuild_for_type RepositoryRegistry.branch_repository, branches, :branch
52
+ end
53
+
54
+ def changesets_for_root(project, branch)
55
+ changesets = session.query "#{ROOT_BRANCH_QUERY} MATCH b-[:changesets]->(c:changeset) RETURN c AS `changeset`, ID(b) as `neo_id` ORDER BY c.id",
56
+ project: project.name,
57
+ path: branch.path
58
+
59
+ rebuild_for_type RepositoryRegistry.changeset_repository, changesets, :changeset
60
+ end
61
+
62
+ def activity(project)
63
+ changesets = session.query "#{ACTIVITY_QUERY} RETURN c as `changeset`, ID(b) as `neo_id`",
64
+ name: project.name
65
+
66
+ rebuild_for_type RepositoryRegistry.changeset_repository, changesets, :changeset
67
+ end
68
+
69
+ def activity_by_date(project, time)
70
+ changesets = session.query "#{ACTIVITY_QUERY} AND c.created >= {time} RETURN c as `changeset`, ID(b) as `neo_id`",
71
+ { name: project.name, time: time.utc.to_i }
72
+
73
+ rebuild_for_type RepositoryRegistry.changeset_repository, changesets, :changeset
74
+ end
75
+
76
+ private
77
+ def rebuild_for_type(repo, data, key)
78
+ data.each_slice(2).map do |data,id|
79
+ repo.rebuild_from_query data[key]['data'], id[:neo_id]
80
+ end
81
+ end
82
+
83
+ def fetch_existing_record(obj)
84
+ find_by_name(obj.name).db_object
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,26 @@
1
+ module TFSGraph
2
+ class Behaviors
3
+ class RelatedRepository
4
+ module Branch
5
+ def find_in_project(project, path)
6
+ project.branches.detect {|b| b.path == path }
7
+ end
8
+
9
+ def absolute_root_for(branch)
10
+ root = branch
11
+ proj = project_for_branch branch
12
+
13
+ until(root.master?) do
14
+ root = proj.branches.detect {|b| b.path == root.root }
15
+ end
16
+
17
+ root
18
+ end
19
+
20
+ def project_for_branch(branch)
21
+ RepositoryRegistry.instance.project_repository.find_by_name branch.project
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ module TFSGraph
2
+ class Behaviors
3
+ class RelatedRepository
4
+ module Changeset
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,48 @@
1
+ module TFSGraph
2
+ class Behaviors
3
+ class RelatedRepository
4
+ module Project
5
+ def create(args)
6
+ obj = super
7
+ relate :projects, root, obj.db_object
8
+
9
+ obj
10
+ end
11
+
12
+ def all
13
+ get_nodes(root, :outgoing, :projects, TFSGraph::Project)
14
+ end
15
+
16
+ def active
17
+ all.reject &:hidden?
18
+ end
19
+
20
+ def find_by_name(name)
21
+ project = all.detect {|p| p.name == name }
22
+ raise TFSGraph::Repository::NotFound, "No project found for #{name}" if project.nil?
23
+
24
+ project
25
+ end
26
+
27
+ def branches_for_root(project, root)
28
+ project.branches.select {|b| b.root == branch.path || b.original_path == branch.path}
29
+ end
30
+
31
+ def changesets_for_root(project, root)
32
+ branches_for_root(project, root).map(&:changesets).flatten.sort
33
+ end
34
+
35
+ def activity(project)
36
+ project.branches.map {|b| b.changesets }.flatten
37
+ end
38
+
39
+ def activity_by_date(project, date)
40
+ activity = activity(project)
41
+ activity = activity.select {|c| c.created > date } unless date.nil?
42
+
43
+ activity
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,10 +1,9 @@
1
- require 'tfs_graph/entity'
1
+ require 'tfs_graph/persistable_entity'
2
2
  require 'tfs_graph/tfs_helpers'
3
3
 
4
4
  module TFSGraph
5
- class Branch < Entity
5
+ class Branch < PersistableEntity
6
6
  extend TFSHelpers
7
- extend Comparable
8
7
 
9
8
  SCHEMA = {
10
9
  original_path: {key: "Path", type: String},
@@ -12,10 +11,11 @@ module TFSGraph
12
11
  project: {converter: ->(path) { branch_project(path) }, key: "Path", type: String},
13
12
  name: {converter: ->(path) { branch_path_to_name(path) }, key: "Path", type: String},
14
13
  root: {converter: ->(path) { repath_archive(server_path_to_odata_path(path)) if path }, key: "ParentBranch", type: String},
15
- created: {key: "DateCreated", type: DateTime},
14
+ created: {key: "DateCreated", type: Time},
16
15
  type: {default: "Feature", type: Integer},
17
16
  archived: {default: false, type: String},
18
- hidden: {default: false, type: String}
17
+ hidden: {default: false, type: String},
18
+ last_updated: {type: Time, default: Time.at(0).utc}
19
19
  }
20
20
 
21
21
  BRANCH_TYPES = [
@@ -29,7 +29,12 @@ module TFSGraph
29
29
 
30
30
  act_as_entity
31
31
 
32
- before_create :detect_type, :detect_archived
32
+ def initialize(repo, args)
33
+ super
34
+
35
+ detect_type
36
+ detect_archived
37
+ end
33
38
 
34
39
  BRANCH_TYPES.each do |t|
35
40
  define_method "#{t}?".to_sym do
@@ -37,6 +42,14 @@ module TFSGraph
37
42
  end
38
43
  end
39
44
 
45
+ def archived
46
+ @archived.to_s
47
+ end
48
+
49
+ def hidden
50
+ @hidden.to_s
51
+ end
52
+
40
53
  def archived?
41
54
  archived.to_s == "true"
42
55
  end
@@ -45,40 +58,40 @@ module TFSGraph
45
58
  hidden.to_s == "true"
46
59
  end
47
60
 
61
+ def active?
62
+ !hidden? && !archived?
63
+ end
64
+
48
65
  def named_type
49
66
  BRANCH_TYPES[type]
50
67
  end
51
68
 
69
+ def updated!
70
+ @last_updated = Time.now.utc
71
+ save!
72
+ end
73
+
74
+ def updated_since?(date)
75
+ @last_updated > date
76
+ end
77
+
52
78
  def hide!
53
79
  self.hidden = true
54
- save
80
+ save!
55
81
  end
56
82
 
57
83
  def archive!
58
84
  self.archived = true
59
- save
85
+ save!
60
86
  end
61
87
 
62
88
  def rootless?
63
89
  !master? && root.empty?
64
90
  end
65
91
 
66
- def type_index(name)
67
- BRANCH_TYPES.index(name.to_sym)
68
- end
69
-
70
92
  # returns a branch
71
93
  def absolute_root
72
- @absolute_root ||= begin
73
- item = self
74
- proj = ProjectStore.find_cached project
75
-
76
- until(item.master?) do
77
- item = proj.branches.detect {|branch| branch.path == item.root }
78
- end
79
-
80
- item
81
- end
94
+ @absolute_root ||= @repo.absolute_root_for(self)
82
95
  end
83
96
 
84
97
  def branch?
@@ -87,11 +100,27 @@ module TFSGraph
87
100
 
88
101
  # branches this one touches or is touched
89
102
  def related_branches
90
- incoming(:related).options(model: Branch).nodes.to_a.map &:id
103
+ @repo.get_nodes(db_object, :incoming, :related, Branch).map &:id
104
+ end
105
+
106
+ def merged_changesets
107
+ @repo.get_nodes(db_object, :outgoing, :included, Changeset)
108
+ end
109
+
110
+ def add_changeset(changeset)
111
+ # attach branch path
112
+ changeset.branch_path = self.path
113
+ changeset.save!
114
+
115
+ @repo.relate(:changesets, self.db_object, changeset.db_object)
116
+ end
117
+
118
+ def add_child(changeset)
119
+ @repo.relate(:child, self.db_object, changeset.db_object)
91
120
  end
92
121
 
93
122
  def changesets
94
- outgoing(:changesets).options(model: Changeset).nodes.to_a
123
+ @repo.get_nodes(db_object, :outgoing, :changesets, Changeset)
95
124
  end
96
125
 
97
126
  def contributors
@@ -99,7 +128,7 @@ module TFSGraph
99
128
  end
100
129
 
101
130
  def root_changeset
102
- @root ||= outgoing(:child).options(model: Changeset).nodes.to_a.first
131
+ @root = @repo.get_nodes(db_object, :outgoing, :child, Changeset).first if (@root.nil? || @root.empty?)
103
132
  end
104
133
 
105
134
  def last_changeset
@@ -108,20 +137,30 @@ module TFSGraph
108
137
 
109
138
  def ahead_of_master
110
139
  return 0 unless absolute_root
111
- self.outgoing(:changesets)
112
- .diff(absolute_root.outgoing(:included)
113
- .intersect(self.outgoing(:changesets)))
114
- .to_a.count
140
+ my_changes = changesets
141
+ root_changes = absolute_root.merged_changesets
142
+
143
+ # get intersection between root and this branch
144
+ intersect = root_changes & my_changes
145
+ # get difference of intersect with my changes
146
+ diff = my_changes - intersect
147
+
148
+ diff.count
115
149
  end
116
150
 
117
151
  # gets the set of changesets that exist in both root and self
118
152
  # then gets a diff of that set and the root.
119
153
  def behind_master
120
154
  return 0 unless absolute_root
121
- absolute_root.outgoing(:changesets)
122
- .diff(self.outgoing(:included)
123
- .intersect(absolute_root.outgoing(:changesets)))
124
- .to_a.count
155
+ my_changes = merged_changesets
156
+ root_changes = absolute_root.changesets
157
+
158
+ # get intersect between my changes and the root
159
+ intersect = my_changes & root_changes
160
+ # get diff of root changes to intersect
161
+ diff = root_changes - intersect
162
+
163
+ diff.count
125
164
  end
126
165
 
127
166
  def <=>(other)
@@ -129,20 +168,28 @@ module TFSGraph
129
168
  end
130
169
 
131
170
  def as_json(options={})
132
- options.merge! methods: :related_branches
133
- super
171
+ results = super
172
+ results[:related_branches] = related_branches
173
+ results[:id] = id
174
+
175
+ results
176
+ end
177
+
178
+ def type_index(name)
179
+ BRANCH_TYPES.index(name.to_sym)
134
180
  end
135
181
 
136
182
  private
183
+
137
184
  def detect_type
138
- return self.type = type_index(:master) if (root.empty?)
185
+ return self.type = type_index(:master) if (root.nil? || root.empty?)
139
186
  return self.type = type_index(:release) if !(name =~ RELEASE_MATCHER).nil?
140
187
  self.type = type_index(:feature)
141
188
  nil
142
189
  end
143
190
 
144
191
  def detect_archived
145
- self.archived = ARCHIVED_FLAGS.any? {|flag| original_path.include? flag }
192
+ self.archived = ARCHIVED_FLAGS.any? {|flag| original_path && original_path.include?(flag) }
146
193
  nil
147
194
  end
148
195