n4j 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
6
+ tmp/
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ rvm gemset use ${PWD##*/}
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rake', '>0.9'
4
+
5
+ # Specify your gem's dependencies in n4j.gemspec
6
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = N4j
2
+
3
+ Some help for modeling Neo4j Rest server.
4
+
5
+ Decided to rush and break this out so I could use it on several little projects.
data/Rakefile ADDED
@@ -0,0 +1,137 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.test_files = FileList['test/*_test.rb']
7
+ test.verbose = true
8
+ # test.warning = true
9
+ end
10
+
11
+
12
+ namespace :n4j do
13
+ desc "Create n4j.yml"
14
+ task :create_n4j_config do
15
+ require 'psych'
16
+ conf_file = Psych.dump({'development' => {'port' => 7480, 'secure_port' => 7481},
17
+ 'test' => {'port' => 7481, 'secure_port' => 7483}})
18
+ FileUtils.mkdir_p 'config'
19
+ File.open('config/n4j.yml', 'w') {|f| f.write(conf_file) }
20
+ end
21
+
22
+ desc "Clear development database."
23
+ task :clear_development do
24
+ port = YAML.parse_file('config/n4j.yml').to_ruby['development']['port']
25
+ result = `curl --request DELETE http://localhost:#{port}/cleandb/all-gone`
26
+ puts result
27
+ end
28
+
29
+ desc "Status of Neo4j servers"
30
+ task :status do
31
+ number_of_neo4j_instances = `ps aux | grep ne[o]4j | wc -l`
32
+ number_of_neo4j_instances.strip!
33
+ puts "#{number_of_neo4j_instances} total instances of Neo4j running, system wide."
34
+ unless number_of_neo4j_instances.to_i < 1
35
+ YAML.parse_file("config/n4j.yml").to_ruby.each_pair do |environment, conf|
36
+ pid_filename = "tmp/pids/n4j_#{environment}_pid"
37
+ if File.exist?(pid_filename)
38
+ pid = IO.read "tmp/pids/n4j_#{environment}_pid"
39
+ pid.chomp!
40
+ result = `ps p#{pid} | grep #{pid} | wc -l`
41
+ result.strip!
42
+ puts "#{result} instances running for #{environment} (pid: #{pid})."
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ desc 'Start servers'
49
+ task :start do |task_name|
50
+ require 'erb'
51
+
52
+ YAML.parse_file('config/n4j.yml').to_ruby.each_pair do |environment, conf|
53
+ @environment = environment
54
+ @port = conf['port']
55
+ @secure_port = conf['secure_port']
56
+ @server_version = 1.6
57
+
58
+ neo4j_bin_link = `which neo4j`
59
+ neo4j_bin_link.chomp!
60
+ neo4j_bin = Pathname.new(neo4j_bin_link).realpath
61
+
62
+ java_command = `which java`
63
+ java_command.chomp!
64
+
65
+ libexec = Pathname.new(neo4j_bin_link).realpath.dirname.dirname
66
+
67
+ libdir = "#{libexec}/lib"
68
+ syslib = "#{libexec}/system/lib"
69
+ plugin_dir = "#{libexec}/plugins"
70
+ n4j_plugins = 'neo4j_plugins'
71
+ neo4j_home = "tmp/n4j/#{@environment}"
72
+ pid_file = "tmp/pids/n4j_#{@environment}_pid"
73
+
74
+ FileUtils.mkdir_p neo4j_home
75
+ FileUtils.mkdir_p "#{neo4j_home}/data/log"
76
+ FileUtils.mkdir_p 'tmp/pids'
77
+
78
+ class_path = [libdir, syslib, plugin_dir, n4j_plugins].collect {|dir| Dir.glob(dir + '/*.jar')}.flatten.join(':')
79
+ java_opts = "-server -XX:+DisableExplicitGC -Dorg.neo4j.server.properties=conf/neo4j-server.properties -Djava.util.logging.config.file=conf/logging.properties -Xms3m -Xmx64m"
80
+
81
+ # system "cp lib/n4j/templates/neo4j.properties tmp/n4j/#{@environment}/"
82
+ # system "cp lib/n4j/templates/logging.properties tmp/n4j/#{@environment}/"
83
+ FileUtils.cp 'templates/neo4j.properties', "tmp/n4j/#{@environment}/neo4j.properties"
84
+ FileUtils.cp 'templates/logging.properties', "tmp/n4j/#{@environment}/logging.properties"
85
+
86
+ FileUtils.touch "#{neo4j_home}/data/log/console.log"
87
+
88
+ neo4j_server_properties = ERB.new(IO.read("templates/neo4j-server-#{@server_version}.properties")).result
89
+ neo4j_server_properties_path = "#{neo4j_home}/neo4j-server.properties"
90
+ File.open(neo4j_server_properties_path, 'w') {|f| f.write(neo4j_server_properties) }
91
+
92
+ neo4j_wrapper_conf = ERB.new(IO.read('templates/neo4j-wrapper.conf')).result
93
+ neo4j_wrapper_conf_path = "#{neo4j_home}/neo4j-wrapper.conf"
94
+ File.open(neo4j_wrapper_conf_path, 'w') {|f| f.write(neo4j_wrapper_conf) }
95
+
96
+ launch_neo4j_command = "#{java_command}
97
+ -cp #{class_path}
98
+ -server
99
+ -XX:+DisableExplicitGC
100
+ -Dorg.neo4j.server.properties=#{neo4j_server_properties_path}
101
+ -Djava.util.logging.config.file=#{neo4j_home}/logging.properties
102
+ -Xms3m
103
+ -Xmx64m
104
+ -Dlog4j.configuration=file:#{neo4j_home}/log4j.properties
105
+ -Dorg.neo4j.server.properties=#{neo4j_server_properties_path}
106
+ -Djava.util.logging.config.file=#{neo4j_home}/logging.properties
107
+ -Dneo4j.home=#{neo4j_home}
108
+ -Dneo4j.instance=#{libexec} org.neo4j.server.Bootstrapper
109
+ >> #{neo4j_home}/data/log/console.log 2>&1 & echo $! > \"#{pid_file}\"
110
+ "
111
+
112
+ system launch_neo4j_command.gsub(/\n/,' ').gsub(/\s+/, ' ')
113
+ puts "#{environment.capitalize} Neo4j launched."
114
+ end
115
+ end
116
+
117
+ desc "Stop server"
118
+ task :stop do
119
+ YAML.parse_file('config/n4j.yml').to_ruby.each_pair do |environment, conf|
120
+ pid_filename = "tmp/pids/n4j_#{environment}_pid"
121
+ if File.exist?(pid_filename)
122
+ pid = IO.read pid_filename
123
+ pid.chomp!
124
+ result = `ps p#{pid} | grep #{pid} | wc -l`
125
+ if result.to_i > 0
126
+ Process.kill("HUP", pid.to_i)
127
+ puts "Killed #{environment} Neo4j."
128
+ else
129
+ puts "#{environment.capitalize} Neo4j wasn't running."
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+
136
+
137
+ end
data/config/n4j.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ development:
3
+ port: 7480
4
+ secure_port: 7481
5
+ test:
6
+ port: 7481
7
+ secure_port: 7483
data/lib/n4j.rb ADDED
@@ -0,0 +1,46 @@
1
+ require "n4j/version"
2
+ require 'active_support/all'
3
+ require 'active_model'
4
+
5
+ module N4j
6
+ autoload :Attributes, 'n4j/attributes'
7
+ autoload :Request, 'n4j/request'
8
+ autoload :Entity, 'n4j/entity'
9
+ autoload :Node, 'n4j/node'
10
+ autoload :Relationship, 'n4j/relationship'
11
+ autoload :Field, 'n4j/field'
12
+ autoload :Traversal, 'n4j/traversal'
13
+ autoload :Cypher, 'n4j/cypher'
14
+
15
+ include Request
16
+
17
+ module Dummy
18
+ extend ActiveSupport::Concern
19
+ included do
20
+ end
21
+ module ClassMethods
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ class GenericNode
28
+ include N4j::Entity
29
+ include N4j::Node
30
+
31
+ end
32
+
33
+ class RootNode < GenericNode
34
+ skip_callback :initialize, :after, :relate_to_root
35
+ end
36
+
37
+ class GenericRelationship
38
+ include N4j::Entity
39
+ include N4j::Relationship
40
+
41
+ def self.find_by_node_path(path)
42
+ result = N4j.batch([{:to => path ,:method => 'GET'}])
43
+ result.first['body'].collect {|r| GenericRelationship.new(r) }
44
+ end
45
+ end
46
+
@@ -0,0 +1,48 @@
1
+ module N4j::Attributes
2
+ extend ActiveSupport::Concern
3
+
4
+ include ActiveModel::AttributeMethods
5
+ include ActiveModel::Dirty
6
+
7
+ included do
8
+ attribute_method_suffix('=')
9
+ end
10
+
11
+ module ClassMethods
12
+ def attributes
13
+ @attributes ||= Set.new
14
+ end
15
+
16
+ def attribute(name)
17
+ attributes << name.to_s
18
+ define_attribute_methods [name.to_s]
19
+ end
20
+ end
21
+
22
+ def attribute(key)
23
+ instance_variable_get("@#{key}")
24
+ end
25
+
26
+ def attribute=(key,value)
27
+ # puts "Setting attribute #{key}!"
28
+ send("#{key}_will_change!") unless value == send(key)
29
+ instance_variable_set("@#{key}", value)
30
+ end
31
+
32
+ def attributes
33
+ self.class.attributes.inject({}) do |hsh, attr|
34
+ hsh[attr] = send(attr)
35
+ hsh
36
+ end
37
+ end
38
+
39
+ def load_attributes(new_attributes)
40
+ self.place_in_batch = nil
41
+ self.class.attributes.each do |key|
42
+ unless new_attributes[key].blank?
43
+ value = new_attributes[key].dup
44
+ instance_variable_set("@#{key}", value)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,41 @@
1
+ module N4j::Node::AutoRelate
2
+ extend ActiveSupport::Concern
3
+ included do
4
+ define_singleton_method "#{model_name.param_key}_root" do
5
+ model_root
6
+ end
7
+ after_initialize :relate_to_root
8
+ end
9
+ module ClassMethods
10
+ def model_root
11
+ # Turned off cache here until I can figure out how to keep this from messing with tests.
12
+ # root = instance_variable_get("@#{model_root_name}")
13
+ # return root if root
14
+
15
+ root = follow_path_from_root || create_root_node
16
+ instance_variable_set("@#{model_root_name}", root)
17
+ end
18
+
19
+ def model_root_name
20
+ variable_name = model_name.param_key
21
+ model_root_name = "#{variable_name}_root"
22
+ end
23
+
24
+ def follow_path_from_root
25
+ service_root.relationships('outgoing_relationships', model_root_name).first.try(:end)
26
+ end
27
+
28
+ def create_root_node
29
+ node = RootNode.new({:name => model_name.human})
30
+ node.new_relationships << GenericRelationship.new(:start => service_root, :end => node, :type => "#{model_root_name}")
31
+ node.save
32
+ node
33
+ end
34
+ end
35
+
36
+ def relate_to_root
37
+ unless persisted?
38
+ new_relationships << GenericRelationship.new(:start => self.class.model_root, :end => self, :type => 'is_a')
39
+ end
40
+ end
41
+ end
data/lib/n4j/cypher.rb ADDED
@@ -0,0 +1,45 @@
1
+ class N4j::Cypher
2
+ PARAMETERS = [:start, :match, :where, :return]
3
+ attr_accessor *PARAMETERS
4
+
5
+ def initialize(opts ={})
6
+ opts.each_pair do |k,v|
7
+ send "#{k}=", v if respond_to?("#{k}=")
8
+ end
9
+ end
10
+
11
+ def next(type = nil) # hops?
12
+ start_var = start.sub(/start\s+/,'').sub(/\s*=.+/,'')
13
+ self.match = "match (#{start_var})-->(b)"
14
+ self
15
+ end
16
+
17
+ def start=(entity)
18
+ @start = if entity.kind_of?(String)
19
+ entity
20
+ else
21
+ "start a = #{entity.entity_type}(#{entity.to_key.first})"
22
+ end
23
+ end
24
+
25
+ def return
26
+ @return || (match && "return #{match.split(/\W+/).last}")
27
+ end
28
+
29
+ def go
30
+ self.class.query(to_query)
31
+ end
32
+
33
+ def to_query
34
+ raise "start must contain 'start' (currently: '#{start}')" if start && !start.index('start')
35
+ raise "match must contain 'match' (currently: '#{match}')" if match && !match.index('match')
36
+ raise "where must contain 'where' (currently: '#{where}')" if where && !where.index('where')
37
+ raise "return must contain 'return' (currently: '#{self.return}')" if self.return && !self.return.index('return')
38
+
39
+ "#{start} #{match} #{where} #{self.return}"
40
+ end
41
+
42
+ def self.query(query)
43
+ N4j.batch([{:to => '/ext/CypherPlugin/graphdb/execute_query', :method => 'POST', :body => {'query' => query}}]).first['body']
44
+ end
45
+ end
data/lib/n4j/entity.rb ADDED
@@ -0,0 +1,117 @@
1
+ module N4j
2
+ module Entity
3
+
4
+ autoload :Representation, 'n4j/representation'
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ extend ActiveModel::Naming
10
+ include ActiveModel::Conversion
11
+ include N4j::Attributes
12
+ include N4j::Field
13
+
14
+ extend ActiveModel::Callbacks
15
+ define_model_callbacks :initialize
16
+
17
+ include Representation
18
+
19
+ attr_accessor :from_neo4j
20
+ attr_accessor :destroyed
21
+ attribute :data
22
+ end
23
+
24
+ module ClassMethods
25
+ end
26
+
27
+ def initialize(hsh)
28
+ run_callbacks :initialize do
29
+ if N4j.neo4j_hash?(hsh)
30
+ load_neo4j_data(hsh)
31
+ load_attributes(from_neo4j)
32
+ end
33
+ end
34
+ end
35
+
36
+ def note_update!
37
+ changed_attributes.clear
38
+ end
39
+
40
+ def load_neo4j_data(hsh)
41
+ changed_attributes.clear
42
+ if hsh # update requests do not return data
43
+ self.from_neo4j = HashWithIndifferentAccess.new.merge(hsh)
44
+ end
45
+ end
46
+
47
+ def from_neo4j_relative
48
+ # Use try here?
49
+ @from_neo4j_relative ||= Hash.new do |hash,key|
50
+ from_neo4j &&
51
+ from_neo4j[key].try(:sub, /http.+\/db\/data/, '')
52
+ end
53
+ end
54
+
55
+ def persisted?
56
+ N4j.neo4j_hash?(from_neo4j) && !destroyed?
57
+ end
58
+
59
+ def needs_persist?
60
+ !persisted? || changed?
61
+ end
62
+
63
+ def destroyed?
64
+ @destroyed
65
+ end
66
+
67
+ def destroy
68
+ destroyable = destroy_bundle.select(&:persisted?)
69
+ commands = destroyable.collect(&:destroy_hash)
70
+ results = N4j.batch(commands)
71
+ destroy_bundle.each {|entity| entity.destroyed = true }
72
+ end
73
+
74
+ def save
75
+ updateable_bundle = create_bundle.select(&:persist_hash)
76
+ commands = updateable_bundle.collect {|entity| entity.persist_hash(:id => entity.place_in_batch) }
77
+ results = N4j.batch(commands)
78
+ updateable_bundle.zip(results).each do |entity,result|
79
+ entity.load_neo4j_data(result['body'])
80
+ entity.post_save
81
+ end
82
+ end
83
+
84
+ def prerequisites ; [] ; end
85
+ def dependent ; [] ; end
86
+ def connected_unsaved ; [] ; end
87
+ def post_save ; [] ; end
88
+
89
+ # Self and related nodes/relationships
90
+ def create_bundle(accumulated_bundle = [])
91
+ unless accumulated_bundle.detect {|entity| entity == self || entity.path == path }
92
+ accumulated_bundle = prerequisites.inject(accumulated_bundle) {|bundle, entity| entity.create_bundle(bundle) }
93
+ self.place_in_batch = accumulated_bundle.length
94
+ accumulated_bundle << self
95
+ connected_unsaved.inject(accumulated_bundle) {|bundle, entity| entity.create_bundle(bundle) }
96
+ end
97
+ accumulated_bundle
98
+ end
99
+
100
+ def destroy_bundle(accumulated_bundle = [])
101
+ unless accumulated_bundle.detect {|entity| entity == self || entity.path == path }
102
+ accumulated_bundle = dependent.inject(accumulated_bundle) {|bundle, entity| entity.destroy_bundle(bundle) }
103
+ accumulated_bundle << self
104
+ end
105
+ accumulated_bundle
106
+ end
107
+
108
+ def place_in_batch=(num)
109
+ @place_in_batch = num
110
+ end
111
+
112
+ def place_in_batch
113
+ @place_in_batch
114
+ end
115
+ end
116
+
117
+ end