keymaker 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/Gemfile +4 -0
- data/LICENSE.md +22 -0
- data/README.md +86 -0
- data/Rakefile +123 -0
- data/keymaker.gemspec +94 -0
- data/keymaker_integration_spec.rb +182 -0
- data/lib/keymaker.rb +43 -0
- data/lib/keymaker/add_node_to_index_request.rb +9 -0
- data/lib/keymaker/batch_get_nodes_request.rb +19 -0
- data/lib/keymaker/configuration.rb +90 -0
- data/lib/keymaker/create_node_request.rb +11 -0
- data/lib/keymaker/create_relationship_request.rb +26 -0
- data/lib/keymaker/delete_relationship_request.rb +17 -0
- data/lib/keymaker/execute_cypher_request.rb +9 -0
- data/lib/keymaker/execute_gremlin_request.rb +9 -0
- data/lib/keymaker/indexing.rb +34 -0
- data/lib/keymaker/node.rb +111 -0
- data/lib/keymaker/path_traverse_request.rb +34 -0
- data/lib/keymaker/remove_node_from_index_request.rb +9 -0
- data/lib/keymaker/request.rb +34 -0
- data/lib/keymaker/response.rb +38 -0
- data/lib/keymaker/serialization.rb +46 -0
- data/lib/keymaker/service.rb +147 -0
- data/lib/keymaker/update_node_properties_request.rb +15 -0
- data/spec/lib/keymaker_integration_spec.rb +189 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/keymaker.rb +106 -0
- metadata +194 -0
data/lib/keymaker.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'active_model'
|
4
|
+
|
5
|
+
require 'keymaker/request'
|
6
|
+
require 'keymaker/response'
|
7
|
+
require 'keymaker/configuration'
|
8
|
+
require 'keymaker/service'
|
9
|
+
|
10
|
+
require 'keymaker/add_node_to_index_request'
|
11
|
+
require 'keymaker/batch_get_nodes_request'
|
12
|
+
require 'keymaker/create_node_request'
|
13
|
+
require 'keymaker/create_relationship_request'
|
14
|
+
require 'keymaker/delete_relationship_request'
|
15
|
+
require 'keymaker/execute_cypher_request'
|
16
|
+
require 'keymaker/execute_gremlin_request'
|
17
|
+
require 'keymaker/path_traverse_request'
|
18
|
+
require 'keymaker/remove_node_from_index_request'
|
19
|
+
require 'keymaker/update_node_properties_request'
|
20
|
+
|
21
|
+
require 'keymaker/indexing'
|
22
|
+
require 'keymaker/serialization'
|
23
|
+
require 'keymaker/node'
|
24
|
+
|
25
|
+
module Keymaker
|
26
|
+
|
27
|
+
VERSION = "0.0.1"
|
28
|
+
|
29
|
+
def self.service
|
30
|
+
@service ||= Keymaker::Service.new(Keymaker::Configuration.new)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.configure
|
34
|
+
@configuration = Keymaker::Configuration.new
|
35
|
+
yield @configuration
|
36
|
+
@service = Keymaker::Service.new(@configuration)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.configuration
|
40
|
+
@configuration
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Keymaker
|
2
|
+
|
3
|
+
class BatchGetNodesRequest < Request
|
4
|
+
|
5
|
+
def submit
|
6
|
+
service.post(batch_path, batch_get_nodes_properties)
|
7
|
+
end
|
8
|
+
|
9
|
+
def batch_get_nodes_properties
|
10
|
+
[].tap do |batch_request|
|
11
|
+
opts.each_with_index do |node_id, request_id|
|
12
|
+
batch_request << {id: request_id, to: node_uri(node_id), method: "GET"}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "addressable/uri"
|
2
|
+
|
3
|
+
module Keymaker
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
attr_accessor :protocol, :server, :port,
|
7
|
+
:data_directory, :cypher_path, :gremlin_path,
|
8
|
+
:log_file, :log_enabled, :logger,
|
9
|
+
:authentication, :username, :password
|
10
|
+
|
11
|
+
def initialize(attrs={})
|
12
|
+
self.protocol = attrs.fetch(:protocol) {'http'}
|
13
|
+
self.server = attrs.fetch(:server) {'localhost'}
|
14
|
+
self.port = attrs.fetch(:port) {7474}
|
15
|
+
self.data_directory = attrs.fetch(:data_directory) {'db/data'}
|
16
|
+
self.cypher_path = attrs.fetch(:cypher_path) {'cypher'}
|
17
|
+
self.gremlin_path = attrs.fetch(:gremlin_path) {'ext/GremlinPlugin/graphdb/execute_script'}
|
18
|
+
self.authentication = attrs.fetch(:authentication) {{}}
|
19
|
+
self.username = attrs.fetch(:username) {nil}
|
20
|
+
self.password = attrs.fetch(:password) {nil}
|
21
|
+
end
|
22
|
+
|
23
|
+
def service_root
|
24
|
+
service_root_url.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def service_root_url
|
28
|
+
Addressable::URI.new(url_opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def url_opts
|
32
|
+
{}.tap do |url_opts|
|
33
|
+
url_opts[:scheme] = protocol
|
34
|
+
url_opts[:host] = server
|
35
|
+
url_opts[:port] = port
|
36
|
+
url_opts[:user] = username if username
|
37
|
+
url_opts[:password] = password if password
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def full_cypher_path
|
42
|
+
[service_root, data_directory, cypher_path].join("/")
|
43
|
+
end
|
44
|
+
|
45
|
+
def full_gremlin_path
|
46
|
+
[service_root, data_directory, gremlin_path].join("/")
|
47
|
+
end
|
48
|
+
|
49
|
+
def node_path
|
50
|
+
[data_directory, "node"].join("/")
|
51
|
+
end
|
52
|
+
|
53
|
+
def node_properties_path(node_id)
|
54
|
+
[node_path, node_id.to_s, "properties"].join("/")
|
55
|
+
end
|
56
|
+
|
57
|
+
def path_traverse_node_path(node_id)
|
58
|
+
[node_path, node_id.to_s, "traverse", "path"].join("/")
|
59
|
+
end
|
60
|
+
|
61
|
+
def batch_node_path(node_id)
|
62
|
+
["/node", node_id.to_s].join("/")
|
63
|
+
end
|
64
|
+
|
65
|
+
def batch_path
|
66
|
+
[data_directory, "batch"].join("/")
|
67
|
+
end
|
68
|
+
|
69
|
+
def relationship_path(relationship_id)
|
70
|
+
[data_directory, "relationship", relationship_id.to_s].join("/")
|
71
|
+
end
|
72
|
+
|
73
|
+
def relationships_path_for_node(node_id)
|
74
|
+
[node_path, node_id.to_s, "relationships"].join("/")
|
75
|
+
end
|
76
|
+
|
77
|
+
def node_full_index_path(index_name, key, value, node_id)
|
78
|
+
[node_index_path(index_name), key, value, node_id].join("/")
|
79
|
+
end
|
80
|
+
|
81
|
+
def node_index_path(index_name)
|
82
|
+
[data_directory, "index", "node", index_name.to_s].join("/")
|
83
|
+
end
|
84
|
+
|
85
|
+
def node_uri(node_id)
|
86
|
+
[service_root, node_path, node_id.to_s].join("/")
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Keymaker
|
2
|
+
|
3
|
+
# Example request
|
4
|
+
|
5
|
+
# POST http://localhost:7474/db/data/node/85/relationships
|
6
|
+
# Accept: application/json
|
7
|
+
# Content-Type: application/json
|
8
|
+
# {"to" : "http://localhost:7474/db/data/node/84", "type" : "LOVES", "data" : {"foo" : "bar"}}
|
9
|
+
|
10
|
+
class CreateRelationshipRequest < Request
|
11
|
+
|
12
|
+
def submit
|
13
|
+
service.post(relationships_path_for_node(opts[:node_id]), rel_properties)
|
14
|
+
end
|
15
|
+
|
16
|
+
def rel_properties
|
17
|
+
{}.tap do |properties|
|
18
|
+
properties[:to] = node_uri(opts[:end_node_id])
|
19
|
+
properties[:type] = opts[:rel_type]
|
20
|
+
properties[:data] = opts[:data] if opts[:data]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Keymaker
|
2
|
+
|
3
|
+
# Example request
|
4
|
+
|
5
|
+
# DELETE http://localhost:7474/db/data/relationship/85
|
6
|
+
# Accept: application/json
|
7
|
+
# Content-Type: application/json
|
8
|
+
|
9
|
+
class DeleteRelationshipRequest < Request
|
10
|
+
|
11
|
+
def submit
|
12
|
+
service.delete(relationship_path(opts[:relationship_id]))
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Keymaker
|
2
|
+
|
3
|
+
module Indexing
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def index_row(index_name)
|
12
|
+
indices_traits[index_name] = indices_traits.fetch(index_name, [])
|
13
|
+
end
|
14
|
+
|
15
|
+
# index :threds, on: :name, with: :sanitized_name
|
16
|
+
# data structure:
|
17
|
+
# { threds: [{ :index_key => :name, :value => :sanitized_name }], users: [{ :index_key => :email, :value => :email }, { :index_key => :username, :value => :username }] }
|
18
|
+
def index(index_name,options)
|
19
|
+
index_row(index_name.to_s) << { :index_key => options[:on].to_s, :value => options.fetch(:with, options[:on]) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def update_indices
|
24
|
+
self.class.indices_traits.each do |index,traits|
|
25
|
+
traits.each do |trait|
|
26
|
+
neo_service.remove_node_from_index(index, trait[:index_key], send(trait[:value]), node_id)
|
27
|
+
neo_service.add_node_to_index(index, trait[:index_key], send(trait[:value]), node_id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Keymaker
|
4
|
+
|
5
|
+
module Node
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ActiveModel::Callbacks
|
9
|
+
base.extend ClassMethods
|
10
|
+
|
11
|
+
base.class_eval do
|
12
|
+
attr_writer :new_node
|
13
|
+
include ActiveModel::MassAssignmentSecurity
|
14
|
+
|
15
|
+
include Keymaker::Indexing
|
16
|
+
include Keymaker::Serialization
|
17
|
+
|
18
|
+
attr_protected :created_at, :updated_at
|
19
|
+
end
|
20
|
+
|
21
|
+
base.after_save :update_indices
|
22
|
+
|
23
|
+
base.class_attribute :property_traits
|
24
|
+
base.class_attribute :indices_traits
|
25
|
+
|
26
|
+
base.property_traits = {}
|
27
|
+
base.indices_traits = {}
|
28
|
+
|
29
|
+
base.property :active_record_id, Integer
|
30
|
+
base.property :node_id, Integer
|
31
|
+
base.property :created_at, DateTime
|
32
|
+
base.property :updated_at, DateTime
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
|
37
|
+
extend Forwardable
|
38
|
+
|
39
|
+
def_delegator :Keymaker, :service, :neo_service
|
40
|
+
|
41
|
+
def properties
|
42
|
+
property_traits.keys
|
43
|
+
end
|
44
|
+
|
45
|
+
def property(attribute,type=String)
|
46
|
+
property_traits[attribute] = type
|
47
|
+
attr_accessor attribute
|
48
|
+
end
|
49
|
+
|
50
|
+
def execute_cypher(query, params={}, return_type=:results_only)
|
51
|
+
executed_query = neo_service.execute_query(query, params)
|
52
|
+
if executed_query.present?
|
53
|
+
case return_type
|
54
|
+
when :results_only
|
55
|
+
executed_query["data"].flatten
|
56
|
+
# TODO: Make this less specific
|
57
|
+
when :full_user
|
58
|
+
{"user" => executed_query["data"].flatten[0]["data"], "neo_id" => executed_query["data"].flatten[1]}
|
59
|
+
end
|
60
|
+
else
|
61
|
+
return []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(attrs)
|
68
|
+
@new_node = true
|
69
|
+
process_attrs(attrs) if attrs.present?
|
70
|
+
end
|
71
|
+
|
72
|
+
def neo_service
|
73
|
+
self.class.neo_service
|
74
|
+
end
|
75
|
+
|
76
|
+
def new?
|
77
|
+
@new_node
|
78
|
+
end
|
79
|
+
|
80
|
+
def sanitize(attrs)
|
81
|
+
serializable_hash(except: :node_id).merge(attrs.except('node_id')).reject {|k,v| v.blank?}
|
82
|
+
end
|
83
|
+
|
84
|
+
def save
|
85
|
+
create_or_update
|
86
|
+
end
|
87
|
+
|
88
|
+
def create_or_update
|
89
|
+
run_callbacks :save do
|
90
|
+
new? ? create : update(attributes)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def create
|
95
|
+
run_callbacks :create do
|
96
|
+
neo_service.create_node(sanitize(attributes)).on_success do |response|
|
97
|
+
self.node_id = response.neo4j_id
|
98
|
+
self.new_node = false
|
99
|
+
self
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def update(attrs)
|
105
|
+
process_attrs(sanitize(attrs.merge(updated_at: Time.now.utc.to_i)))
|
106
|
+
neo_service.update_node_properties(node_id, sanitize(attributes))
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Keymaker
|
2
|
+
class PathTraverseRequest < Request
|
3
|
+
|
4
|
+
# Example request
|
5
|
+
|
6
|
+
# POST http://localhost:7474/db/data/node/9/traverse/path
|
7
|
+
# Accept: application/json
|
8
|
+
# Content-Type: application/json
|
9
|
+
# {"order":"breadth_first","uniqueness":"none","return_filter":{"language":"builtin","name":"all"}}
|
10
|
+
|
11
|
+
def submit
|
12
|
+
service.post(path_traverse_node_path(opts[:node_id]), path_traverse_properties)
|
13
|
+
end
|
14
|
+
|
15
|
+
def path_traverse_properties
|
16
|
+
# :order - breadth_first or depth_first
|
17
|
+
# :relationships - all, in, or out
|
18
|
+
# :uniqueness - node_global, none, relationship_global, node_path, or relationship_path
|
19
|
+
# :prune_evaluator
|
20
|
+
# :return_filter
|
21
|
+
# :max_depth
|
22
|
+
|
23
|
+
{}.tap do |properties|
|
24
|
+
properties[:order] = opts.fetch(:order, "breadth_first")
|
25
|
+
properties[:relationships] = opts.fetch(:relationships, "all")
|
26
|
+
properties[:uniqueness] = opts.fetch(:uniqueness, "relationship_global")
|
27
|
+
properties[:prune_evaluator] = opts[:prune_evaluator] if opts[:prune_evaluator]
|
28
|
+
properties[:return_filter] = opts[:return_filter] if opts[:return_filter]
|
29
|
+
properties[:max_depth] = opts[:max_depth] if opts[:max_depth]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Keymaker
|
2
|
+
class Request
|
3
|
+
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
|
7
|
+
def_delegator :config, :batch_node_path
|
8
|
+
def_delegator :config, :batch_path
|
9
|
+
def_delegator :config, :full_cypher_path
|
10
|
+
def_delegator :config, :full_gremlin_path
|
11
|
+
def_delegator :config, :node_full_index_path
|
12
|
+
def_delegator :config, :node_index_path
|
13
|
+
def_delegator :config, :node_path
|
14
|
+
def_delegator :config, :node_properties_path
|
15
|
+
def_delegator :config, :node_uri
|
16
|
+
def_delegator :config, :path_traverse_node_path
|
17
|
+
def_delegator :config, :relationship_path
|
18
|
+
def_delegator :config, :relationships_path_for_node
|
19
|
+
|
20
|
+
def_delegator :response, :body
|
21
|
+
def_delegator :response, :status
|
22
|
+
def_delegator :response, :faraday_response
|
23
|
+
def_delegator :response, :faraday_response=
|
24
|
+
|
25
|
+
attr_accessor :service, :config, :opts
|
26
|
+
|
27
|
+
def initialize(service, options)
|
28
|
+
self.config = service.config
|
29
|
+
self.opts = options
|
30
|
+
self.service = service
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|