graphable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ RVM_STRING="rvm use ruby-1.9.3-p125-perf@graphable --create --verbose"
2
+ [ -e './.rvmrc_local' ] && source './.rvmrc_local' || $RVM_STRING
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in graphable.gemspec
4
+ gemspec
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ task :default do
5
+ require 'graphable'
6
+ puts "Loaded!"
7
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/graphable/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Joe Fredette"]
6
+ gem.email = ["jfredett@gmail.com"]
7
+ gem.description = %q{A library for extracting static graph representations of data from rails-y databases}
8
+ gem.summary = %q{A library for extracting static graph representations of data from rails-y databases}
9
+ gem.homepage = "http://www.github.com/jfredett/graphable"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "graphable"
15
+ gem.require_paths = ["lib"]
16
+
17
+ gem.add_dependency "active_support"
18
+ gem.add_dependency "neography"
19
+
20
+ gem.version = Graphable::VERSION
21
+ end
@@ -0,0 +1,85 @@
1
+ require 'active_support'
2
+ Dir["./lib/graphable/*.rb"].each do |f| require f end
3
+
4
+
5
+ module Graphable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ Graphable.register NodeCreator.new(self)
10
+ indexes :id
11
+ end
12
+
13
+ def self.register(registrant)
14
+ registry << registrant
15
+ end
16
+
17
+ def self.neo
18
+ Neography::Rest.new(ENV["NEO4J_URL"] || "http://localhost:7474")
19
+ end
20
+
21
+ def self.registry
22
+ @registry ||= []
23
+ end
24
+
25
+ def self.build!
26
+ puts "Building Graph"
27
+ registry.select { |f| f.is_a? NodeCreator }.map(&:call)
28
+ registry.select { |f| f.is_a? IndexCreator }.map(&:call)
29
+ registry.select { |f| f.is_a? EdgeCreator }.map(&:call)
30
+ nil
31
+ end
32
+
33
+ def self.index_cache
34
+ @index_cache ||= {}
35
+ end
36
+
37
+ def self.completed_indicies
38
+ @completed_indicies ||= {}
39
+ end
40
+
41
+ def self.completed_index(klass, method)
42
+ completed_indicies[[klass, method]] = true
43
+ end
44
+
45
+ def self.has_indexed?(klass, method)
46
+ completed_indicies[[klass,method]]
47
+ end
48
+
49
+
50
+
51
+ module InstanceMethods
52
+ def to_node
53
+ attributes.to_hash.tap do |hash|
54
+ hash.each { |k, _| hash.delete(k) if k.to_s =~ /_id$/ } #remove FKs
55
+ hash[:type] = self.class.name
56
+ end
57
+ end
58
+ end
59
+
60
+ module ClassMethods
61
+ def index_name
62
+ "#{name.downcase.pluralize}_index"
63
+ end
64
+
65
+ def indexes(*methods)
66
+ methods.each do |method|
67
+ Graphable.register IndexCreator.new(self, method)
68
+ end
69
+ end
70
+
71
+ def has_edge(hash = {}, &block)
72
+ source_klass = self
73
+ target_klass = hash[:to]
74
+ hash[:block] = block
75
+ if hash.has_key?(:through)
76
+ Graphable.register EdgeCreator.through(source_klass, target_klass, hash.delete(:through), hash)
77
+ elsif hash.has_key?(:via)
78
+ Graphable.register EdgeCreator.via(source_klass, target_klass, hash.delete(:via), hash)
79
+ else
80
+ raise "Invalid Edge type, must be :through or :via"
81
+ end
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,102 @@
1
+ require 'forwardable'
2
+
3
+ module Graphable
4
+ class EdgeCreator
5
+ extend Forwardable
6
+
7
+ def self.through(*args)
8
+ # for has_many :through relations
9
+ ThroughEdgeCreator.new(*args)
10
+ end
11
+
12
+ def self.via(*args)
13
+ #for habtm/has_many relations
14
+ ViaEdgeCreator.new(*args)
15
+ end
16
+
17
+ def initialize(source, target, method, opts)
18
+ @source = source
19
+ @target = target
20
+ @method = method
21
+ @name = opts[:name] || method
22
+ @metadata_proc = opts[:block]
23
+ @target_name = opts[:target_method]
24
+ end
25
+
26
+ protected
27
+
28
+ def load_node(n)
29
+ binding.pry unless n
30
+ Graphable.index_cache[n]
31
+ end
32
+
33
+ def build_relationship(source_node, target_node, metadata = {})
34
+ # dodges an issue with neography, nil-values can't be sent up. that's
35
+ # okay, since trying to access a field which doesn't exist should be nil
36
+ # anyway.
37
+ metadata.reject! { |_,v| v.nil? }
38
+
39
+ Neography::Relationship.create(@name, source_node, target_node, metadata)
40
+ end
41
+
42
+ delegate :all => :@source
43
+ alias_method :sources, :all
44
+
45
+ def target_name
46
+ @target_name ||= @target.name.pluralize.downcase.to_sym
47
+ end
48
+ end
49
+
50
+ class ThroughEdgeCreator < EdgeCreator
51
+ def call
52
+ puts "Building edges for #{@source.name} -> #{@target.name}"
53
+ sources.each_slice(100) do |slice|
54
+ slice.each do |source|
55
+ source_node = load_node(source)
56
+
57
+ intermediates_for(source).each do |intermediate_target|
58
+ metadata = @metadata_proc.call(intermediate_target) if @metadata_proc
59
+ metadata = intermediate_target.send(:edge_metadata) if intermediate_target.respond_to?(:edge_metadata)
60
+
61
+ target_node = load_node(intermediate_target.send(target_name)) rescue binding.pry
62
+
63
+ build_relationship(source_node, target_node, metadata || {})
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def targets_for(source)
70
+ res = intermediates_for(source).map(&target_name)
71
+ res = res.to_a if res.respond_to?(:to_a)
72
+ if res.respond_to?(:each) then res else [res] end
73
+ end
74
+
75
+ def intermediates_for(source)
76
+ source.send(@method)
77
+ end
78
+ end
79
+
80
+ class ViaEdgeCreator < EdgeCreator
81
+ def call
82
+ puts "Building edges for #{@source.name} -> #{@target.name}"
83
+ sources.each do |source|
84
+ source_node = load_node(source)
85
+
86
+ targets_for(source).each do |target|
87
+ metadata = @metadata_proc.call(target) if @metadata_proc
88
+
89
+ target_node = load_node(target) rescue binding.pry
90
+ build_relationship(source_node, target_node, metadata || {})
91
+ end
92
+ end
93
+ end
94
+
95
+ def targets_for(source)
96
+ res = source.send(@method)
97
+ res = res.to_a if res.respond_to?(:to_a)
98
+ if res.respond_to?(:each) then res else [res] end
99
+ end
100
+ end
101
+
102
+ end
@@ -0,0 +1,26 @@
1
+ module Graphable
2
+ class IndexCreator
3
+ extend Forwardable
4
+
5
+ attr_reader :klass
6
+ delegate [:name, :all, :index_name] => :@klass
7
+
8
+ def initialize(klass, method)
9
+ @klass = klass
10
+ @method = method
11
+ end
12
+
13
+ def call
14
+ return if Graphable.has_indexed?(@klass, @method)
15
+
16
+ puts "Building index for #{name}"
17
+ Graphable.neo.create_node_index(index_name, 'fulltext')
18
+
19
+ all.to_a.each do |object|
20
+ Graphable.neo.add_node_to_index(index_name, @method, object.send(@method), Graphable.index_cache[object])
21
+ end
22
+
23
+ Graphable.completed_index(@klass, @method)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ require 'forwardable'
2
+
3
+ module Graphable
4
+ class NodeCreator
5
+ extend Forwardable
6
+
7
+ attr_reader :klass
8
+ delegate [:name, :all] => :@klass
9
+
10
+ def initialize(klass)
11
+ @klass = klass
12
+ end
13
+
14
+ def call
15
+ puts "Building nodes for #{name}"
16
+ all.each_slice(100) do |slice|
17
+ slice.each do |object|
18
+ Graphable.index_cache[object] = Neography::Node.create(object.to_node)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Graphable
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joe Fredette
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: active_support
16
+ requirement: &2152759120 !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: *2152759120
25
+ - !ruby/object:Gem::Dependency
26
+ name: neography
27
+ requirement: &2152758480 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2152758480
36
+ description: A library for extracting static graph representations of data from rails-y
37
+ databases
38
+ email:
39
+ - jfredett@gmail.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - .rvmrc
46
+ - Gemfile
47
+ - Rakefile
48
+ - graphable.gemspec
49
+ - lib/graphable.rb
50
+ - lib/graphable/edge_creator.rb
51
+ - lib/graphable/index_creator.rb
52
+ - lib/graphable/node_creator.rb
53
+ - lib/graphable/version.rb
54
+ homepage: http://www.github.com/jfredett/graphable
55
+ licenses: []
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 1.8.17
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: A library for extracting static graph representations of data from rails-y
78
+ databases
79
+ test_files: []