hari 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 +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
|