n4j 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.
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