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
@@ -1,30 +1,21 @@
1
- require 'tfs_graph/tfs_client'
1
+ require 'tfs_graph/abstract_store'
2
+
2
3
  require 'tfs_graph/project/project_normalizer'
4
+ require 'tfs_graph/abstract_store'
3
5
 
4
6
  module TFSGraph
5
- class ProjectStore
6
- extend TFSClient
7
-
8
- class << self
9
- def cache
10
- projects = tfs.projects.run
11
- normalized = ProjectNormalizer.normalize_many projects
12
-
13
- normalized.map do |project_attrs|
14
- project = Project.create project_attrs
15
- Related::Relationship.create(:projects, Related.root, project)
16
-
17
- project
18
- end
19
- end
7
+ class ProjectStore < AbstractStore
8
+ def cache(project)
9
+ RepositoryRegistry.project_repository.create project
10
+ end
20
11
 
21
- def all_cached
22
- Related.root.outgoing(:projects).options(model: Project).nodes.to_a
23
- end
12
+ private
13
+ def root_query
14
+ tfs.projects
15
+ end
24
16
 
25
- def find_cached(name)
26
- all_cached.detect {|p| p.name == name }
27
- end
17
+ def normalize(projects)
18
+ ProjectNormalizer.normalize_many projects
28
19
  end
29
20
  end
30
21
  end
@@ -0,0 +1,78 @@
1
+ require 'active_support/hash_with_indifferent_access'
2
+ require 'tfs_graph/extensions'
3
+
4
+ require 'tfs_graph/project'
5
+ require 'tfs_graph/branch'
6
+ require 'tfs_graph/changeset'
7
+
8
+ require 'tfs_graph/behaviors'
9
+
10
+ module TFSGraph
11
+ class Repository
12
+ include Extensions
13
+ attr_reader :type
14
+
15
+ NotFound = Class.new(RuntimeError)
16
+
17
+ def initialize(type)
18
+ @type = type
19
+
20
+ add_behavior self, constantize("TFSGraph::Behaviors::#{self.base_class_name}::#{type.base_class_name}")
21
+
22
+ # register self as the server type
23
+ ServerRegistry.server(self)
24
+ end
25
+
26
+ def find(id)
27
+ rebuild find_native(id)
28
+ end
29
+
30
+ def exists?(id)
31
+ begin
32
+ find_native(id)
33
+ true
34
+ rescue NotFound
35
+ false
36
+ end
37
+ end
38
+
39
+ def related?(node1, node2, type)
40
+ node1.rels(dir: :outgoing, between: node2, type: type).any?
41
+ end
42
+
43
+ def save(object)
44
+ db_object = object.persisted? ? update(object) : persist(object)
45
+ object.persist get_id(db_object), db_object
46
+ end
47
+
48
+ def delete(obj)
49
+ obj.db_object = nil
50
+ obj.id = nil
51
+ end
52
+
53
+ def build(args={})
54
+ @type.new self, args
55
+ end
56
+
57
+ def rebuild(db_object)
58
+ attributes = HashWithIndifferentAccess.new db_object.attributes
59
+
60
+ obj = build attributes
61
+ obj.persist get_id(db_object), db_object
62
+ end
63
+
64
+ def create(args)
65
+ object = build(args)
66
+ save(object)
67
+ end
68
+
69
+ def inspect
70
+ type
71
+ end
72
+
73
+ private
74
+ def normalize(attrs)
75
+ HashWithIndifferentAccess.new attrs
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,97 @@
1
+ require 'neo4j-core'
2
+ require 'tfs_graph/repository'
3
+
4
+ module TFSGraph
5
+ class Repository
6
+ class Neo4jRepository < Repository
7
+ def find_native(id)
8
+ node = Neo4j::Label.query(type.base_class_name.downcase.to_sym, conditions: {id: id}).to_a.first
9
+ node ||= find_by_neo_id(id)
10
+
11
+ raise NotFound, id unless node
12
+ node
13
+ end
14
+
15
+ def find_by_neo_id(id)
16
+ Neo4j::Node.load(id)
17
+ end
18
+
19
+ def session
20
+ Neo4j::Session.current
21
+ end
22
+
23
+ def flush
24
+ @root = nil
25
+ end
26
+
27
+ def delete(obj)
28
+ obj.db_object.del
29
+ super
30
+ end
31
+
32
+ def drop_all
33
+ flush
34
+ session.query("MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r")
35
+ end
36
+
37
+ def root
38
+ @root ||= begin
39
+ node = Neo4j::Label.find_all_nodes(:root).first
40
+ node = Neo4j::Node.create({name: "Root node"}, :root) if node.nil?
41
+ node
42
+ end
43
+ end
44
+
45
+ def relate(relationship, parent, child)
46
+ Neo4j::Relationship.create relationship, parent, child unless related?(parent, child, relationship)
47
+ end
48
+
49
+ def get_nodes(entity, direction, relation, type)
50
+ begin
51
+ entity.nodes(dir: direction.to_sym, type: relation.to_sym).map do |node|
52
+ type.repository.rebuild node
53
+ end
54
+ rescue Neo4j::Server::CypherResponse::ResponseError => e
55
+ []
56
+ end
57
+ end
58
+
59
+ def rebuild(db_object)
60
+ attributes = normalize db_object.props
61
+
62
+ obj = build attributes
63
+ obj.persist get_id(db_object), db_object
64
+ end
65
+
66
+ def rebuild_from_query(attrs, id)
67
+ obj = build normalize(attrs)
68
+ obj.persist id, nil
69
+ end
70
+
71
+ private
72
+ # persist and update both expose the DB object from Neo4j
73
+ # make methods private so we have to use save to persist
74
+
75
+ # create the DB object
76
+ def persist(object)
77
+ begin
78
+ Neo4j::Node.create(object.to_hash, object.base_class_name.downcase)
79
+ rescue Neo4j::Server::CypherResponse::ResponseError => e
80
+ # assume all errors come from constraint errors... probably a bad idea
81
+ fetch_existing_record(object)
82
+ end
83
+ end
84
+
85
+ # update the DB object
86
+ def update(object)
87
+ object.db_object.update_props object.attributes
88
+ object.db_object
89
+ end
90
+
91
+ def get_id(object)
92
+ return 0 if object.nil?
93
+ object.neo_id
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,89 @@
1
+ require 'related'
2
+ require 'tfs_graph/repository'
3
+
4
+ module TFSGraph
5
+ class Repository
6
+ class RelatedRepository < Repository
7
+ def initialize(type)
8
+ super
9
+ Related.redis = ServerRegistry.redis
10
+ end
11
+
12
+ def find_native(id)
13
+ begin
14
+ Related::Node.find(id)
15
+ rescue Related::NotFound => e
16
+ raise TFSGraph::Repository::NotFound, e.message
17
+ end
18
+ end
19
+
20
+ def root
21
+ Related.root
22
+ end
23
+
24
+ def session
25
+ ServerRegistry.redis
26
+ end
27
+
28
+ def flush
29
+ # noop
30
+ end
31
+
32
+ def drop_all
33
+ flush
34
+ session.keys("*").each do |k|
35
+ session.del k
36
+ end
37
+ end
38
+
39
+ def delete(obj)
40
+ obj.db_object.destroy
41
+ super
42
+ end
43
+
44
+ def relate(relationship, parent, child)
45
+ Related::Relationship.create relationship, parent, child
46
+ end
47
+
48
+ def get_nodes(entity, direction, relation, type)
49
+ get_nodes_for(get_relation(entity, direction, relation), type)
50
+ end
51
+
52
+ def get_nodes_for(relation, type)
53
+ relation.nodes.map do |node|
54
+ type.repository.rebuild node
55
+ end
56
+ end
57
+
58
+ def get_relation(entity, direction, relation)
59
+ entity.send(direction.to_sym, relation.to_sym)
60
+ end
61
+
62
+ private
63
+ # persist and update both expose the DB object from Redis/Related
64
+ # make methods private so we have to use save to persist
65
+
66
+ # create the DB object
67
+ def persist(object)
68
+ Related::Node.create(object.to_hash)
69
+ end
70
+
71
+ # update the DB object
72
+ def update(object)
73
+ db_object = object.db_object
74
+
75
+ object.attributes.each do |key, value|
76
+ db_object.write_attribute key, value
77
+ end
78
+
79
+ db_object.save
80
+ db_object
81
+ end
82
+
83
+ def get_id(object)
84
+ return 0 if object.nil?
85
+ object.id
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,60 @@
1
+ require 'singleton'
2
+ require 'tfs_graph/extensions'
3
+
4
+ module TFSGraph
5
+ class RepositoryRegistry
6
+ include Extensions
7
+ include Singleton
8
+
9
+ TYPES = %w(branch changeset project)
10
+
11
+ def self.register
12
+ # assume re-registering means we want to clear existing repos
13
+ instance.reset!
14
+ yield instance if block_given?
15
+
16
+ instance
17
+ end
18
+
19
+ def initialize
20
+ reset!
21
+ end
22
+
23
+ def reset!
24
+ @base_repo = nil
25
+
26
+ TYPES.each do |type|
27
+ instance_variable_set repo_memo(type), nil
28
+ end
29
+ end
30
+
31
+ def identifier
32
+ (@base_repo.name =~ /Neo4j/i) ? "neo4j" : "redis"
33
+ end
34
+
35
+ def type(type)
36
+ @base_repo = type
37
+ end
38
+
39
+ TYPES.each do |type|
40
+ define_method "#{type}_repository" do
41
+ existing = instance_variable_get repo_memo(type)
42
+ return existing unless existing.nil?
43
+
44
+ repo = @base_repo.new constantize("TFSGraph::#{type.capitalize}")
45
+
46
+ instance_variable_set(repo_memo(type), repo)
47
+ repo
48
+ end
49
+
50
+ define_singleton_method "#{type}_repository" do
51
+ instance.send "#{type}_repository"
52
+ end
53
+ end
54
+
55
+ private
56
+ def repo_memo(type)
57
+ "@#{type}_repo"
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,45 @@
1
+ require 'redis-namespace'
2
+
3
+ module TFSGraph
4
+ class ServerRegistry
5
+ include Singleton
6
+
7
+ DEFAULT_REDIS = {url: "redis://localhost:6379", namespace: "tfs_graph" }
8
+
9
+ def reset!
10
+ @redis = nil
11
+ end
12
+
13
+ def self.register
14
+ instance.reset!
15
+
16
+ yield instance if block_given?
17
+ instance
18
+ end
19
+
20
+ def initialize
21
+ reset!
22
+ end
23
+
24
+ def server(server_obj=nil)
25
+ return @server if @server && server_obj.nil?
26
+ raise ArgumentError, "Need to register a server first" unless server_obj
27
+
28
+ @server = server_obj
29
+ end
30
+
31
+ def redis(url: DEFAULT_REDIS[:url], namespace: DEFAULT_REDIS[:namespace])
32
+ return @redis unless @redis.nil?
33
+
34
+ @redis = Redis::Namespace.new(namespace, redis: Redis.connect(url: url))
35
+ end
36
+
37
+ define_singleton_method :redis do |url: DEFAULT_REDIS[:url], namespace: DEFAULT_REDIS[:namespace]|
38
+ instance.redis url: url, namespace: namespace
39
+ end
40
+
41
+ define_singleton_method :server do |server_obj=nil|
42
+ instance.server server_obj
43
+ end
44
+ end
45
+ end
@@ -1,4 +1,5 @@
1
1
  require 'time'
2
+ require 'tfs_graph/server_registry'
2
3
 
3
4
  module TFSGraph
4
5
  module StoreHelpers
@@ -6,9 +7,7 @@ module TFSGraph
6
7
 
7
8
  # flush by key so that we only disturbe our namespace
8
9
  def flush_all
9
- redis.keys("*").each do |k|
10
- redis.del k
11
- end
10
+ RepositoryRegistry.project_repository.drop_all
12
11
  end
13
12
 
14
13
  def mark_as_updated(time=nil)
@@ -18,14 +17,14 @@ module TFSGraph
18
17
 
19
18
  def last_updated_on
20
19
  date = redis.get(UPDATED_KEY)
21
- return Time.now unless date
20
+ return Time.at(0).localtime unless date
22
21
 
23
22
  Time.parse(date).localtime
24
23
  end
25
24
 
26
25
  private
27
26
  def redis
28
- Related.redis
27
+ ServerRegistry.redis
29
28
  end
30
29
  end
31
30
  end