hari 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +0 -0
- data/Rakefile +1 -0
- data/hari.gemspec +38 -0
- data/lib/hari/configuration/redis.rb +33 -0
- data/lib/hari/configuration.rb +12 -0
- data/lib/hari/entity/property/builder.rb +25 -0
- data/lib/hari/entity/property.rb +24 -0
- data/lib/hari/entity/repository.rb +75 -0
- data/lib/hari/entity/serialization/boolean.rb +43 -0
- data/lib/hari/entity/serialization/date.rb +21 -0
- data/lib/hari/entity/serialization/datetime.rb +21 -0
- data/lib/hari/entity/serialization/float.rb +19 -0
- data/lib/hari/entity/serialization/integer.rb +28 -0
- data/lib/hari/entity/serialization/string.rb +17 -0
- data/lib/hari/entity/serialization/time.rb +21 -0
- data/lib/hari/entity/serialization.rb +42 -0
- data/lib/hari/entity.rb +77 -0
- data/lib/hari/errors.rb +17 -0
- data/lib/hari/node/queries.rb +7 -0
- data/lib/hari/node.rb +16 -0
- data/lib/hari/relationship/linked_list.rb +22 -0
- data/lib/hari/relationship/sorted_set.rb +22 -0
- data/lib/hari/relationship.rb +72 -0
- data/lib/hari/scripts.rb +26 -0
- data/lib/hari/version.rb +3 -0
- data/lib/hari.rb +20 -0
- data/spec/hari/entity/repository_spec.rb +22 -0
- data/spec/hari/entity/serialization/boolean_spec.rb +32 -0
- data/spec/hari/entity/serialization/date_spec.rb +23 -0
- data/spec/hari/entity/serialization/datetime_spec.rb +23 -0
- data/spec/hari/entity/serialization/float_spec.rb +19 -0
- data/spec/hari/entity/serialization/integer_spec.rb +18 -0
- data/spec/hari/entity/serialization/string_spec.rb +13 -0
- data/spec/hari/entity/serialization/time_spec.rb +14 -0
- data/spec/hari/entity/serialization_spec.rb +24 -0
- data/spec/spec_helper.rb +10 -0
- metadata +256 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Victor Rodrigues
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/hari.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'hari/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |s|
|
9
|
+
s.name = 'hari'
|
10
|
+
s.version = Hari::VERSION
|
11
|
+
s.summary = 'Hari is a graph library on top of Redis + Lua scripts'
|
12
|
+
s.description = <<-MD
|
13
|
+
Hari is a graph library on top of Redis database + Lua scripts
|
14
|
+
MD
|
15
|
+
|
16
|
+
s.author = 'Victor Rodrigues'
|
17
|
+
s.email = 'victorc.rodrigues@gmail.com'
|
18
|
+
s.homepage = 'http://github.com/rodrigues/hari'
|
19
|
+
|
20
|
+
s.license = 'MIT'
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split($/)
|
23
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
25
|
+
s.require_paths = ['lib']
|
26
|
+
|
27
|
+
s.add_dependency 'redis', '~> 3.0'
|
28
|
+
s.add_dependency 'redis-namespace', '~> 1.2'
|
29
|
+
s.add_dependency 'hiredis', '~> 0.4'
|
30
|
+
s.add_dependency 'activemodel', '~> 3.2'
|
31
|
+
s.add_dependency 'activesupport', '~> 3.2'
|
32
|
+
s.add_dependency 'yajl-ruby', '~> 1.1'
|
33
|
+
|
34
|
+
s.add_development_dependency 'bundler', '~> 1.3'
|
35
|
+
s.add_development_dependency 'rspec', '~> 2.13'
|
36
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
37
|
+
s.add_development_dependency 'pry'
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Hari
|
2
|
+
module Configuration
|
3
|
+
module Redis
|
4
|
+
|
5
|
+
def redis
|
6
|
+
@redis || begin
|
7
|
+
self.redis = 'localhost:6379'
|
8
|
+
@redis
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def redis=(server)
|
13
|
+
@redis = redis_namespace(server)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def redis_namespace(server)
|
19
|
+
return server if server.kind_of?(::Redis::Namespace)
|
20
|
+
|
21
|
+
::Redis::Namespace.new :hari, redis_server(server)
|
22
|
+
end
|
23
|
+
|
24
|
+
def redis_server(server)
|
25
|
+
return server unless server.kind_of?(::String)
|
26
|
+
|
27
|
+
host, port = server.split(':')
|
28
|
+
::Redis.new host: host, port: port
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
class Property
|
4
|
+
module Builder
|
5
|
+
|
6
|
+
def property(name, options = {})
|
7
|
+
attr_accessor name
|
8
|
+
validates_presence_of name if options[:required]
|
9
|
+
|
10
|
+
self.properties << Property.new(name, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def properties(*args)
|
14
|
+
options = args.extract_options!
|
15
|
+
args.each { |name| property name, options }
|
16
|
+
|
17
|
+
@properties ||= begin
|
18
|
+
self == Hari::Entity ? [] : Hari::Entity.properties.dup
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'hari/entity/property/builder'
|
2
|
+
|
3
|
+
module Hari
|
4
|
+
class Entity
|
5
|
+
class Property
|
6
|
+
|
7
|
+
attr_accessor :name, :serializer, :options
|
8
|
+
|
9
|
+
def initialize(name, options = {})
|
10
|
+
@name, @options = name.to_s, options
|
11
|
+
@serializer = options.delete(:type) || Serialization::String
|
12
|
+
end
|
13
|
+
|
14
|
+
def serialize(value)
|
15
|
+
serializer.serialize(value, name: name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def desserialize(value)
|
19
|
+
serializer.desserialize(value, name: name)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Repository
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def create_or_update
|
7
|
+
run_callbacks(:save) { new? ? create : update }
|
8
|
+
end
|
9
|
+
|
10
|
+
alias save create_or_update
|
11
|
+
|
12
|
+
def create
|
13
|
+
run_callbacks :create do
|
14
|
+
fail Hari::ValidationsFailed, self unless valid?
|
15
|
+
|
16
|
+
@id ||= generate_id
|
17
|
+
self.created_at = Time.now
|
18
|
+
self.updated_at = Time.now
|
19
|
+
persist
|
20
|
+
end
|
21
|
+
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def update
|
26
|
+
run_callbacks :update do
|
27
|
+
fail Hari::ValidationsFailed, self unless valid?
|
28
|
+
|
29
|
+
self.updated_at = Time.now.utc.iso8601
|
30
|
+
persist
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def persist
|
37
|
+
Hari.redis.set id, to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete
|
41
|
+
run_callbacks :destroy do
|
42
|
+
Hari.redis.del id
|
43
|
+
@destroyed = true
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
alias destroy delete
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
|
53
|
+
def create(attrs = {})
|
54
|
+
new(attrs).save
|
55
|
+
end
|
56
|
+
|
57
|
+
def find(*args)
|
58
|
+
options = args.extract_options!
|
59
|
+
args.flatten!
|
60
|
+
args.one? ? find_one(args[0], options) : find_many(args, options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_one(id, options = {})
|
64
|
+
from_json Hari.redis.get(id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_many(ids, options = {})
|
68
|
+
Hari.redis.mget(ids).map &method(:from_json)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Serialization
|
4
|
+
module Boolean
|
5
|
+
|
6
|
+
MAPPINGS = {
|
7
|
+
true => true,
|
8
|
+
'true' => true,
|
9
|
+
'TRUE' => true,
|
10
|
+
'1' => true,
|
11
|
+
1 => true,
|
12
|
+
1.0 => true,
|
13
|
+
'x' => true,
|
14
|
+
'X' => true,
|
15
|
+
't' => true,
|
16
|
+
'T' => true,
|
17
|
+
false => false,
|
18
|
+
'false' => false,
|
19
|
+
'FALSE' => false,
|
20
|
+
'0' => false,
|
21
|
+
0 => false,
|
22
|
+
0.0 => false,
|
23
|
+
'' => false,
|
24
|
+
' ' => false,
|
25
|
+
'f' => false,
|
26
|
+
'F' => false,
|
27
|
+
nil => false
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
def self.serialize(value, options = {})
|
31
|
+
desserialize value, options
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.desserialize(value, options = {})
|
35
|
+
MAPPINGS[value].tap do |bool|
|
36
|
+
fail SerializationError, "#{options[:name]}:#{value} is not boolean" if bool.nil?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Serialization
|
4
|
+
module Date
|
5
|
+
|
6
|
+
def self.serialize(value, options = {})
|
7
|
+
desserialize(value, options).try :iso8601
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.desserialize(value, options = {})
|
11
|
+
return unless value.present?
|
12
|
+
|
13
|
+
value.kind_of?(::Date) ? value : ::Date.parse(value)
|
14
|
+
rescue
|
15
|
+
raise SerializationError, "#{options[:name]}:#{value} is an invalid date"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Serialization
|
4
|
+
module DateTime
|
5
|
+
|
6
|
+
def self.serialize(value, options = {})
|
7
|
+
desserialize(value, options).try :iso8601
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.desserialize(value, options = {})
|
11
|
+
return unless value.present?
|
12
|
+
|
13
|
+
value.kind_of?(::DateTime) ? value : ::DateTime.parse(value)
|
14
|
+
rescue
|
15
|
+
raise SerializationError, "#{options[:name]}:#{value} is an invalid date time"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Serialization
|
4
|
+
module Float
|
5
|
+
|
6
|
+
def self.serialize(value, options = {})
|
7
|
+
desserialize value, options
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.desserialize(value, options = {})
|
11
|
+
Float value
|
12
|
+
rescue
|
13
|
+
raise SerializationError, "#{options[:name]}:#{value} is not a float"
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Serialization
|
4
|
+
module Integer
|
5
|
+
|
6
|
+
def self.serialize(value, options = {})
|
7
|
+
desserialize value, options
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.desserialize(value, options = {})
|
11
|
+
case value
|
12
|
+
when ::String
|
13
|
+
if value =~ /^\d+$/
|
14
|
+
value.to_i
|
15
|
+
else
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
else
|
19
|
+
Integer(value)
|
20
|
+
end
|
21
|
+
rescue
|
22
|
+
raise SerializationError, "#{options[:name]}:#{value} is not an integer"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Serialization
|
4
|
+
module Time
|
5
|
+
|
6
|
+
def self.serialize(value, options = {})
|
7
|
+
desserialize(value, options).try :strftime, '%Y%m%d%H%M%S'
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.desserialize(value, options = {})
|
11
|
+
return unless value.present?
|
12
|
+
|
13
|
+
value.kind_of?(::Time) ? value : ::Time.parse(value)
|
14
|
+
rescue
|
15
|
+
raise SerializationError, "#{options[:name]}:#{value} is an invalid time"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'hari/entity/serialization/boolean'
|
2
|
+
require 'hari/entity/serialization/date'
|
3
|
+
require 'hari/entity/serialization/datetime'
|
4
|
+
require 'hari/entity/serialization/float'
|
5
|
+
require 'hari/entity/serialization/integer'
|
6
|
+
require 'hari/entity/serialization/string'
|
7
|
+
require 'hari/entity/serialization/time'
|
8
|
+
|
9
|
+
module Hari
|
10
|
+
class Entity
|
11
|
+
module Serialization
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
def to_json
|
15
|
+
hash = self.class.properties.inject({}) do |buffer, prop|
|
16
|
+
buffer.merge prop.name => prop.serialize(send(prop.name))
|
17
|
+
end
|
18
|
+
|
19
|
+
Yajl::Encoder.encode hash
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
|
24
|
+
def from_json(source)
|
25
|
+
return if source.blank?
|
26
|
+
|
27
|
+
attrs = Yajl::Parser.parse(source).inject({}) do |buffer, (key, value)|
|
28
|
+
if prop = properties.find { |p| p.name == key }
|
29
|
+
buffer[key] = prop.desserialize(value)
|
30
|
+
end
|
31
|
+
|
32
|
+
buffer
|
33
|
+
end
|
34
|
+
|
35
|
+
new attrs
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/hari/entity.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'hari/entity/property'
|
2
|
+
require 'hari/entity/repository'
|
3
|
+
require 'hari/entity/serialization'
|
4
|
+
|
5
|
+
module Hari
|
6
|
+
class Entity
|
7
|
+
extend ActiveModel::Naming
|
8
|
+
extend ActiveModel::Callbacks
|
9
|
+
include ActiveModel::Validations
|
10
|
+
extend Property::Builder
|
11
|
+
include Repository
|
12
|
+
include Serialization
|
13
|
+
|
14
|
+
define_model_callbacks :create, :update, :destroy, :save
|
15
|
+
|
16
|
+
property :id
|
17
|
+
property :created_at, type: Time
|
18
|
+
property :updated_at, type: Time
|
19
|
+
|
20
|
+
def initialize(attrs = {})
|
21
|
+
return if attrs.blank?
|
22
|
+
|
23
|
+
attrs = attrs.with_indifferent_access
|
24
|
+
|
25
|
+
self.class.properties.each do |prop|
|
26
|
+
send("#{prop.name}=", attrs[prop.name]) if attrs[prop.name]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def attributes
|
31
|
+
self.class.properties.inject({}) do |buffer, prop|
|
32
|
+
buffer.merge prop.name => send(prop.name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
alias attribute send
|
37
|
+
alias read_attribute send
|
38
|
+
|
39
|
+
def write_attribute(name, value)
|
40
|
+
send "#{name}=", value
|
41
|
+
end
|
42
|
+
|
43
|
+
def has_attribute?(name)
|
44
|
+
respond_to? name
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
other.is_a?(Hari::Entity) && self.id == other.id
|
49
|
+
end
|
50
|
+
|
51
|
+
def new?
|
52
|
+
self.id.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
alias new_record? new?
|
56
|
+
|
57
|
+
def persisted?
|
58
|
+
not new?
|
59
|
+
end
|
60
|
+
|
61
|
+
def destroyed?
|
62
|
+
@destroyed
|
63
|
+
end
|
64
|
+
|
65
|
+
def generate_id
|
66
|
+
'ent' + ::Time.now.strftime('%Y%m%d%H%M%S') + SecureRandom.hex(3)
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
attrs = attributes
|
71
|
+
attrs.delete 'id'
|
72
|
+
|
73
|
+
"<#{self.class} id=#{id} attributes=#{attrs}>"
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
data/lib/hari/errors.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Hari
|
2
|
+
class HariException < RuntimeError; end
|
3
|
+
class NotFound < HariException; end
|
4
|
+
class InvalidQuery < HariException; end
|
5
|
+
class SerializationError < HariException; end
|
6
|
+
|
7
|
+
class ValidationsFailed < HariException
|
8
|
+
attr_reader :object
|
9
|
+
|
10
|
+
def initialize(object)
|
11
|
+
@object = object
|
12
|
+
errors = @object.errors.full_messages.to_sentence
|
13
|
+
super errors
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/hari/node.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'hari/node/queries'
|
2
|
+
|
3
|
+
module Hari
|
4
|
+
class Node < Entity
|
5
|
+
include Hari::Node::Queries
|
6
|
+
|
7
|
+
property :model_id, required: true
|
8
|
+
|
9
|
+
def generate_id
|
10
|
+
return model_id if model_id.include?('#')
|
11
|
+
|
12
|
+
self.class.to_s.underscore + "#" + model_id.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Hari
|
2
|
+
class Relationship < Entity
|
3
|
+
module Backend
|
4
|
+
module LinkedList
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def type
|
8
|
+
:ll
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(rel)
|
12
|
+
%w(in out).each { |d| Hari.redis.lpush rel.key(d), rel.id }
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete(rel)
|
16
|
+
%w(in out).each { |d| Hari.redis.lrem rel.key(d), 1, rel.id }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Hari
|
2
|
+
class Relationship < Entity
|
3
|
+
module Backend
|
4
|
+
module SortedSet
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def type
|
8
|
+
:ss
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(rel)
|
12
|
+
%w(in out).each { |d| Hari.redis.zadd rel.key(d), rel.weight(d), rel.id }
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete(rel)
|
16
|
+
%w(in out).each { |d| Hari.redis.zrem rel.key(d), rel.id }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Hari
|
2
|
+
class Relationship < Entity
|
3
|
+
|
4
|
+
properties :label, :start_node_id, :end_node_id, presence: true
|
5
|
+
|
6
|
+
def generate_id
|
7
|
+
'rel:' + start_node.id + ':' + end_node.id + ':' + backend.id + ':' + SecureRandom.hex(3)
|
8
|
+
end
|
9
|
+
|
10
|
+
def start_node
|
11
|
+
@start_node ||= Hari::Node.find(start_node_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def end_node
|
15
|
+
@end_node ||= Hari::Node.find(end_node_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.create(label, start_node, end_node, attributes = {})
|
19
|
+
attrs = attributes.merge(label: label,
|
20
|
+
start_node_id: node_id(start_node),
|
21
|
+
end_node_id: node_id(end_node))
|
22
|
+
new(attrs).save
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.use!(backend)
|
26
|
+
@backend = backend.kind_of?(Module) ? backend
|
27
|
+
: "Hari::Relationship::#{backend.to_s.camelize}".constantize
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.backend
|
31
|
+
@backend ||= Hari::Relationship::Backend::SortedSet
|
32
|
+
end
|
33
|
+
|
34
|
+
def backend
|
35
|
+
self.class.backend
|
36
|
+
end
|
37
|
+
|
38
|
+
def weight(dir)
|
39
|
+
Time.now
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def node_id(node)
|
45
|
+
node.kind_of?(::String) ? node : node.try(:id)
|
46
|
+
end
|
47
|
+
|
48
|
+
def key(dir = nil)
|
49
|
+
case dir
|
50
|
+
when nil
|
51
|
+
"#{start_node_id}:#{label}:#{end_node_id}"
|
52
|
+
else
|
53
|
+
"#{start_node_id}:#{label}:#{dir}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create
|
58
|
+
super
|
59
|
+
backend.create self
|
60
|
+
Hari.redis.set key, id
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete
|
65
|
+
backend.delete self
|
66
|
+
Hari.redis.del key
|
67
|
+
super
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
data/lib/hari/scripts.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Hari
|
2
|
+
module Scripts
|
3
|
+
|
4
|
+
SCRIPT_PATH = File.expand_path('../scripts', __FILE__)
|
5
|
+
|
6
|
+
def map
|
7
|
+
@scripts ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def load
|
11
|
+
files = Dir[SCRIPT_PATH.join('**/*.lua')]
|
12
|
+
|
13
|
+
files.each do |file|
|
14
|
+
name = file.gsub("#{SCRIPT_PATH}/", '').gsub('.lua', '')
|
15
|
+
script = File.read(file)
|
16
|
+
sha = Hari.redis.script(:load, script)
|
17
|
+
map[name] = sha
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def sha(script_name)
|
22
|
+
map[script_name.to_s]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/hari/version.rb
ADDED
data/lib/hari.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'redis/namespace'
|
3
|
+
require 'active_model'
|
4
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
5
|
+
require 'active_support/core_ext/module/delegation'
|
6
|
+
require 'active_support/core_ext/object/try'
|
7
|
+
require 'yajl'
|
8
|
+
|
9
|
+
require 'hari/version'
|
10
|
+
require 'hari/configuration'
|
11
|
+
require 'hari/errors'
|
12
|
+
require 'hari/entity'
|
13
|
+
require 'hari/node'
|
14
|
+
require 'hari/relationship'
|
15
|
+
|
16
|
+
module Hari
|
17
|
+
extend self
|
18
|
+
extend Configuration
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Repository do
|
4
|
+
|
5
|
+
specify '.create, .find' do
|
6
|
+
model = TestModel.create(name: 'Ze', birth: '2012-04-20', points: '300')
|
7
|
+
model.id.should be
|
8
|
+
|
9
|
+
found = TestModel.find(model.id)
|
10
|
+
found.birth.year.should == 2012
|
11
|
+
found.birth.month.should == 4
|
12
|
+
found.birth.day.should == 20
|
13
|
+
found.points.should == 300
|
14
|
+
found.name.should == 'Ze'
|
15
|
+
|
16
|
+
model2 = TestModel.create(name: 'Jo', birth: '2009-03-21', points: '404')
|
17
|
+
|
18
|
+
founds = TestModel.find(model.id, model2.id)
|
19
|
+
founds.size.should == 2
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization::Boolean do
|
4
|
+
|
5
|
+
describe '.serialize, .desserialize' do
|
6
|
+
it 'truthful values become true' do
|
7
|
+
subject.serialize(true).should be_true
|
8
|
+
subject.serialize('true').should be_true
|
9
|
+
subject.serialize('TRUE').should be_true
|
10
|
+
subject.serialize('1').should be_true
|
11
|
+
subject.serialize(1).should be_true
|
12
|
+
subject.serialize(1.0).should be_true
|
13
|
+
subject.serialize('x').should be_true
|
14
|
+
subject.serialize('X').should be_true
|
15
|
+
subject.serialize('t').should be_true
|
16
|
+
subject.serialize('T').should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'untruthful values become false' do
|
20
|
+
subject.serialize(false).should be_false
|
21
|
+
subject.serialize('false').should be_false
|
22
|
+
subject.serialize('FALSE').should be_false
|
23
|
+
subject.serialize('0').should be_false
|
24
|
+
subject.serialize(0).should be_false
|
25
|
+
subject.serialize(0.0).should be_false
|
26
|
+
subject.serialize('').should be_false
|
27
|
+
subject.serialize(' ').should be_false
|
28
|
+
subject.serialize(nil).should be_false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization::Date do
|
4
|
+
|
5
|
+
describe '.serialize' do
|
6
|
+
it 'converts to date string' do
|
7
|
+
subject.serialize(Date.new(2011, 07, 01)).should == '2011-07-01'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.desserialize' do
|
12
|
+
it 'converts to date' do
|
13
|
+
subject.desserialize('2011-07-01').should == Date.new(2011, 07, 01)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when an invalid value is desserialized' do
|
17
|
+
it 'raises an error' do
|
18
|
+
expect { subject.desserialize('not a date') }.to raise_error
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization::DateTime do
|
4
|
+
|
5
|
+
describe '.serialize' do
|
6
|
+
it 'converts to date time string' do
|
7
|
+
subject.serialize(DateTime.new(2011, 07, 01, 10, 20, 30)).should == '2011-07-01T10:20:30+00:00'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.desserialize' do
|
12
|
+
it 'converts to date time' do
|
13
|
+
subject.desserialize('2011-07-01T10:20:30').should == DateTime.new(2011, 07, 01, 10, 20, 30)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when an invalid value is desserialized' do
|
17
|
+
it 'raises an error' do
|
18
|
+
expect { subject.desserialize('not a date time') }.to raise_error
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization::Float do
|
4
|
+
|
5
|
+
describe '.serialize, .desserialize' do
|
6
|
+
it 'converts to float' do
|
7
|
+
subject.serialize(4237.55).should == 4237.55
|
8
|
+
subject.serialize('4237.55').should == 4237.55
|
9
|
+
subject.serialize('4237').should == 4237.0
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when an invalid value is desserialized' do
|
13
|
+
it 'raises an error' do
|
14
|
+
expect { subject.desserialize('not a float') }.to raise_error
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization::Integer do
|
4
|
+
|
5
|
+
describe '.serialize, .desserialize' do
|
6
|
+
it 'converts to integer' do
|
7
|
+
subject.serialize('1200').should == 1200
|
8
|
+
subject.serialize(1200).should == 1200
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'when an invalid value is serialized' do
|
12
|
+
it 'raises an error' do
|
13
|
+
expect { subject.desserialize('not an integer') }.to raise_error
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization::String do
|
4
|
+
|
5
|
+
describe '.serialize, .desserialize' do
|
6
|
+
it 'converts value to string' do
|
7
|
+
subject.serialize(:something).should == 'something'
|
8
|
+
subject.serialize(1).should == '1'
|
9
|
+
subject.serialize(nil).should == nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization::Time do
|
4
|
+
|
5
|
+
describe '.serialize, .desserialize' do
|
6
|
+
it 'converts value to string' do
|
7
|
+
time = Time.new(2012, 04, 30, 12, 05, 50)
|
8
|
+
subject.serialize(time).should == '20120430120550'
|
9
|
+
subject.serialize('2012-04-30T12:05:50').should == '20120430120550'
|
10
|
+
subject.desserialize('20120430120550').should == time
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization do
|
4
|
+
|
5
|
+
describe '#to_json' do
|
6
|
+
it 'serializes instance to json' do
|
7
|
+
model = TestModel.new(name: 'Ze',
|
8
|
+
birth: Date.new(1986, 01, 23),
|
9
|
+
points: '200')
|
10
|
+
|
11
|
+
model.to_json.should == '{"id":null,"created_at":null,"updated_at":null,"name":"Ze","birth":"1986-01-23","points":200}'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.from_json' do
|
16
|
+
it 'desserializes instance from json' do
|
17
|
+
model = TestModel.from_json('{"name":"Ze","birth":"1986-01-23","points":200}')
|
18
|
+
model.name.should == 'Ze'
|
19
|
+
model.birth.should == Date.new(1986, 01, 23)
|
20
|
+
model.points.should == 200
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hari
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Victor Rodrigues
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: redis-namespace
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.2'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.2'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: hiredis
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.4'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.4'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activemodel
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.2'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.2'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: activesupport
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '3.2'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '3.2'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: yajl-ruby
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '1.1'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1.1'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: bundler
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.3'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '1.3'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rspec
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '2.13'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '2.13'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: rake
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '10.0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '10.0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: pry
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
description: ! ' Hari is a graph library on top of Redis database + Lua scripts
|
175
|
+
|
176
|
+
'
|
177
|
+
email: victorc.rodrigues@gmail.com
|
178
|
+
executables: []
|
179
|
+
extensions: []
|
180
|
+
extra_rdoc_files: []
|
181
|
+
files:
|
182
|
+
- .gitignore
|
183
|
+
- Gemfile
|
184
|
+
- LICENSE
|
185
|
+
- README.md
|
186
|
+
- Rakefile
|
187
|
+
- hari.gemspec
|
188
|
+
- lib/hari.rb
|
189
|
+
- lib/hari/configuration.rb
|
190
|
+
- lib/hari/configuration/redis.rb
|
191
|
+
- lib/hari/entity.rb
|
192
|
+
- lib/hari/entity/property.rb
|
193
|
+
- lib/hari/entity/property/builder.rb
|
194
|
+
- lib/hari/entity/repository.rb
|
195
|
+
- lib/hari/entity/serialization.rb
|
196
|
+
- lib/hari/entity/serialization/boolean.rb
|
197
|
+
- lib/hari/entity/serialization/date.rb
|
198
|
+
- lib/hari/entity/serialization/datetime.rb
|
199
|
+
- lib/hari/entity/serialization/float.rb
|
200
|
+
- lib/hari/entity/serialization/integer.rb
|
201
|
+
- lib/hari/entity/serialization/string.rb
|
202
|
+
- lib/hari/entity/serialization/time.rb
|
203
|
+
- lib/hari/errors.rb
|
204
|
+
- lib/hari/node.rb
|
205
|
+
- lib/hari/node/queries.rb
|
206
|
+
- lib/hari/relationship.rb
|
207
|
+
- lib/hari/relationship/linked_list.rb
|
208
|
+
- lib/hari/relationship/sorted_set.rb
|
209
|
+
- lib/hari/scripts.rb
|
210
|
+
- lib/hari/version.rb
|
211
|
+
- spec/hari/entity/repository_spec.rb
|
212
|
+
- spec/hari/entity/serialization/boolean_spec.rb
|
213
|
+
- spec/hari/entity/serialization/date_spec.rb
|
214
|
+
- spec/hari/entity/serialization/datetime_spec.rb
|
215
|
+
- spec/hari/entity/serialization/float_spec.rb
|
216
|
+
- spec/hari/entity/serialization/integer_spec.rb
|
217
|
+
- spec/hari/entity/serialization/string_spec.rb
|
218
|
+
- spec/hari/entity/serialization/time_spec.rb
|
219
|
+
- spec/hari/entity/serialization_spec.rb
|
220
|
+
- spec/spec_helper.rb
|
221
|
+
homepage: http://github.com/rodrigues/hari
|
222
|
+
licenses:
|
223
|
+
- MIT
|
224
|
+
post_install_message:
|
225
|
+
rdoc_options: []
|
226
|
+
require_paths:
|
227
|
+
- lib
|
228
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
229
|
+
none: false
|
230
|
+
requirements:
|
231
|
+
- - ! '>='
|
232
|
+
- !ruby/object:Gem::Version
|
233
|
+
version: '0'
|
234
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
235
|
+
none: false
|
236
|
+
requirements:
|
237
|
+
- - ! '>='
|
238
|
+
- !ruby/object:Gem::Version
|
239
|
+
version: '0'
|
240
|
+
requirements: []
|
241
|
+
rubyforge_project:
|
242
|
+
rubygems_version: 1.8.25
|
243
|
+
signing_key:
|
244
|
+
specification_version: 3
|
245
|
+
summary: Hari is a graph library on top of Redis + Lua scripts
|
246
|
+
test_files:
|
247
|
+
- spec/hari/entity/repository_spec.rb
|
248
|
+
- spec/hari/entity/serialization/boolean_spec.rb
|
249
|
+
- spec/hari/entity/serialization/date_spec.rb
|
250
|
+
- spec/hari/entity/serialization/datetime_spec.rb
|
251
|
+
- spec/hari/entity/serialization/float_spec.rb
|
252
|
+
- spec/hari/entity/serialization/integer_spec.rb
|
253
|
+
- spec/hari/entity/serialization/string_spec.rb
|
254
|
+
- spec/hari/entity/serialization/time_spec.rb
|
255
|
+
- spec/hari/entity/serialization_spec.rb
|
256
|
+
- spec/spec_helper.rb
|