graphable 0.0.1

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.
@@ -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: []