tfs_graph 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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