hari 0.0.4 → 0.0.5
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.
- checksums.yaml +4 -4
- data/hari.gemspec +10 -2
- data/lib/hari.rb +8 -4
- data/lib/hari/configuration/redis.rb +6 -2
- data/lib/hari/entity.rb +10 -20
- data/lib/hari/entity/property.rb +19 -4
- data/lib/hari/entity/property/builder.rb +18 -3
- data/lib/hari/entity/repository.rb +11 -3
- data/lib/hari/entity/serialization.rb +53 -9
- data/lib/hari/entity/serialization/array.rb +21 -0
- data/lib/hari/entity/serialization/hash.rb +31 -0
- data/lib/hari/keys.rb +5 -0
- data/lib/hari/keys/hash.rb +67 -0
- data/lib/hari/keys/key.rb +24 -3
- data/lib/hari/keys/list.rb +10 -8
- data/lib/hari/keys/set.rb +8 -8
- data/lib/hari/keys/sorted_set.rb +20 -8
- data/lib/hari/node.rb +19 -2
- data/lib/hari/node/index.rb +152 -0
- data/lib/hari/node/queries.rb +32 -17
- data/lib/hari/node/queries/relation.rb +14 -1
- data/lib/hari/node/queries/relation/backend/sorted_set.rb +41 -90
- data/lib/hari/node/queries/relation/backend/sorted_set/count_step.rb +16 -0
- data/lib/hari/node/queries/relation/backend/sorted_set/node_step.rb +91 -0
- data/lib/hari/node/queries/type.rb +69 -4
- data/lib/hari/node/repository.rb +36 -0
- data/lib/hari/node/serialization.rb +11 -10
- data/lib/hari/object.rb +6 -0
- data/lib/hari/serialization.rb +3 -0
- data/lib/hari/version.rb +1 -1
- data/spec/hari/entity/repository_spec.rb +17 -0
- data/spec/hari/entity/serialization/hash_spec.rb +16 -0
- data/spec/hari/entity/serialization_spec.rb +14 -4
- data/spec/hari/keys/hash_spec.rb +55 -0
- data/spec/hari/keys/lists_spec.rb +27 -0
- data/spec/hari/node/index_spec.rb +199 -0
- data/spec/hari/node_spec.rb +84 -0
- data/spec/hari/serialization_spec.rb +41 -0
- data/spec/spec_helper.rb +6 -2
- metadata +27 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca49682c61cc3b4b1a347e83c9d45e9a06747cb9
|
4
|
+
data.tar.gz: 3c89e8825f0286262ac6a7a4a3f0acbaa0a46191
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5c0e5c57e863e065d0fe700d30a143cbab436f44b822ac8cfc26fcc3da0e3547517db2a565d6a402fa737d025851e18e9663f311d5c4d643cced4796d86a71f
|
7
|
+
data.tar.gz: 4d354e66a0ef1b90abf287d2a59bbc836fe25368a5ab590ca2f56b0a14f84d69b6f63fe76a5c01da51e6e0a232179078553404f7c385f9926c1a141828ab09e0
|
data/hari.gemspec
CHANGED
@@ -8,9 +8,17 @@ require 'hari/version'
|
|
8
8
|
Gem::Specification.new do |s|
|
9
9
|
s.name = 'hari'
|
10
10
|
s.version = Hari::VERSION
|
11
|
-
s.summary = '
|
11
|
+
s.summary = 'A tool to abstract complex relationships between ' +
|
12
|
+
'Ruby objects onto Redis data structures.'
|
13
|
+
|
12
14
|
s.description = <<-MD
|
13
|
-
Hari is a
|
15
|
+
Hari is a tool to abstract complex relationships between
|
16
|
+
Ruby objects onto Redis data structures.
|
17
|
+
|
18
|
+
It allows for expressive querying of those relationships
|
19
|
+
as well, in an easy way. It is mostly geared towards
|
20
|
+
typical social networking concepts like news feeds,
|
21
|
+
activity logs, friends of friends, mutual friends, and so on.
|
14
22
|
MD
|
15
23
|
|
16
24
|
s.author = 'Victor Rodrigues'
|
data/lib/hari.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'redis'
|
2
2
|
require 'redis/namespace'
|
3
|
+
require 'redis/connection/hiredis'
|
3
4
|
require 'active_model'
|
4
5
|
require 'active_support/core_ext/hash/indifferent_access'
|
5
6
|
require 'active_support/core_ext/module/delegation'
|
@@ -8,6 +9,7 @@ require 'active_support/core_ext/string/inflections'
|
|
8
9
|
require 'yajl'
|
9
10
|
require 'erb'
|
10
11
|
require 'ostruct'
|
12
|
+
require 'digest/md5'
|
11
13
|
|
12
14
|
require 'hari/version'
|
13
15
|
require 'hari/configuration'
|
@@ -16,10 +18,12 @@ require 'hari/errors'
|
|
16
18
|
module Hari
|
17
19
|
extend self
|
18
20
|
|
19
|
-
autoload :Entity,
|
20
|
-
autoload :Keys,
|
21
|
-
autoload :Node,
|
22
|
-
autoload :
|
21
|
+
autoload :Entity, 'hari/entity'
|
22
|
+
autoload :Keys, 'hari/keys'
|
23
|
+
autoload :Node, 'hari/node'
|
24
|
+
autoload :Object, 'hari/object'
|
25
|
+
autoload :Relation, 'hari/relation'
|
26
|
+
autoload :Serialization, 'hari/serialization'
|
23
27
|
|
24
28
|
extend Configuration
|
25
29
|
extend Hari::Node::Queries
|
@@ -16,9 +16,13 @@ module Hari
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def redis_namespace(server)
|
19
|
-
|
19
|
+
prefix = 'hari'
|
20
20
|
|
21
|
-
::Redis::Namespace
|
21
|
+
if server.kind_of?(::Redis::Namespace)
|
22
|
+
prefix = "#{server.namespace}:#{prefix}"
|
23
|
+
end
|
24
|
+
|
25
|
+
::Redis::Namespace.new prefix, redis_server(server)
|
22
26
|
end
|
23
27
|
|
24
28
|
def redis_server(server)
|
data/lib/hari/entity.rb
CHANGED
@@ -1,18 +1,12 @@
|
|
1
|
-
require 'hari/entity/property'
|
2
|
-
require 'hari/entity/repository'
|
3
|
-
require 'hari/entity/serialization'
|
4
|
-
|
5
1
|
module Hari
|
6
2
|
class Entity
|
7
3
|
extend ActiveModel::Naming
|
8
4
|
extend ActiveModel::Callbacks
|
9
|
-
include ActiveModel::Validations
|
10
5
|
|
11
6
|
autoload :Property, 'hari/entity/property'
|
12
|
-
autoload :Repository, 'hari/entity/
|
13
|
-
autoload :Serialization, 'hari/entity/
|
7
|
+
autoload :Repository, 'hari/entity/repository'
|
8
|
+
autoload :Serialization, 'hari/entity/serialization'
|
14
9
|
|
15
|
-
extend Property::Builder
|
16
10
|
include Repository
|
17
11
|
include Serialization
|
18
12
|
|
@@ -23,27 +17,23 @@ module Hari
|
|
23
17
|
property :updated_at, type: Time
|
24
18
|
|
25
19
|
def initialize(attrs = {})
|
20
|
+
update_attributes attrs, save: false
|
21
|
+
end
|
22
|
+
|
23
|
+
def update_attributes(attrs = {}, options = {})
|
26
24
|
return if attrs.blank?
|
27
25
|
|
28
26
|
attrs = attrs.with_indifferent_access
|
29
27
|
|
30
28
|
self.class.properties.each do |prop|
|
31
|
-
|
29
|
+
write_attribute prop.name, attrs[prop.name] if attrs.key?(prop.name)
|
32
30
|
end
|
33
|
-
end
|
34
31
|
|
35
|
-
|
36
|
-
self.class.properties.inject({}) do |buffer, prop|
|
37
|
-
buffer.merge prop.name => send(prop.name)
|
38
|
-
end
|
32
|
+
save if options.fetch(:save, true)
|
39
33
|
end
|
40
34
|
|
41
|
-
|
42
|
-
|
43
|
-
alias :has_attribute? :respond_to?
|
44
|
-
|
45
|
-
def write_attribute(name, value)
|
46
|
-
send "#{name}=", value
|
35
|
+
def update_attribute(attribute, value)
|
36
|
+
update_attributes attribute => value
|
47
37
|
end
|
48
38
|
|
49
39
|
def ==(other)
|
data/lib/hari/entity/property.rb
CHANGED
@@ -3,14 +3,29 @@ module Hari
|
|
3
3
|
class Property
|
4
4
|
autoload :Builder, 'hari/entity/property/builder'
|
5
5
|
|
6
|
-
attr_accessor :name, :serializer, :options
|
6
|
+
attr_accessor :entity, :name, :serializer, :options
|
7
7
|
|
8
|
-
def initialize(name, options = {})
|
9
|
-
@name, @options = name.to_s, options
|
8
|
+
def initialize(entity, name, options = {})
|
9
|
+
@entity, @name, @options = entity, name.to_s, options
|
10
10
|
@serializer = options.delete(:type) || Serialization::String
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def default
|
14
|
+
case options[:default]
|
15
|
+
when Proc
|
16
|
+
options[:default].call
|
17
|
+
else
|
18
|
+
options[:default]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def serialize(entity)
|
23
|
+
value = entity.attribute(name)
|
24
|
+
|
25
|
+
if value.nil?
|
26
|
+
value = entity.write_attribute(name, default)
|
27
|
+
end
|
28
|
+
|
14
29
|
serializer.serialize value, name: name
|
15
30
|
end
|
16
31
|
|
@@ -4,10 +4,24 @@ module Hari
|
|
4
4
|
module Builder
|
5
5
|
|
6
6
|
def property(name, options = {})
|
7
|
-
|
7
|
+
attr_reader name
|
8
|
+
define_attribute_method name
|
9
|
+
|
8
10
|
validates_presence_of name if options[:required]
|
9
11
|
|
10
|
-
|
12
|
+
define_method "#{name}=" do |value|
|
13
|
+
unless send(name) == value
|
14
|
+
send "#{name}_will_change!"
|
15
|
+
end
|
16
|
+
|
17
|
+
instance_variable_set "@#{name}", value
|
18
|
+
end
|
19
|
+
|
20
|
+
if options[:type] == Serialization::Boolean
|
21
|
+
define_method("#{name}?") { send name }
|
22
|
+
end
|
23
|
+
|
24
|
+
properties << Property.new(self, name, options)
|
11
25
|
end
|
12
26
|
|
13
27
|
def properties(*args)
|
@@ -22,7 +36,8 @@ module Hari
|
|
22
36
|
a.ancestors.include? Hari::Entity
|
23
37
|
end
|
24
38
|
|
25
|
-
entities_ancestors[1]
|
39
|
+
entity = entities_ancestors[1]
|
40
|
+
entity ? entity.properties.dup : []
|
26
41
|
end
|
27
42
|
end
|
28
43
|
end
|
@@ -4,7 +4,9 @@ module Hari
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
def create_or_update
|
7
|
-
run_callbacks(:save) { new? ? create : update }
|
7
|
+
run_callbacks(:save) { new? ? create : update }.tap do
|
8
|
+
@changed_attributes.clear
|
9
|
+
end
|
8
10
|
end
|
9
11
|
|
10
12
|
alias :save :create_or_update
|
@@ -14,7 +16,7 @@ module Hari
|
|
14
16
|
fail Hari::ValidationsFailed, self unless valid?
|
15
17
|
|
16
18
|
@id ||= generate_id
|
17
|
-
|
19
|
+
@created_at ||= Time.now
|
18
20
|
self.updated_at = Time.now
|
19
21
|
persist
|
20
22
|
end
|
@@ -34,7 +36,9 @@ module Hari
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def persist
|
37
|
-
|
39
|
+
source = to_json
|
40
|
+
@previously_changed = changes
|
41
|
+
Hari.redis.set id, source
|
38
42
|
end
|
39
43
|
|
40
44
|
def delete
|
@@ -57,6 +61,8 @@ module Hari
|
|
57
61
|
def find(*args)
|
58
62
|
options = args.extract_options!
|
59
63
|
args.flatten!
|
64
|
+
return if args.empty?
|
65
|
+
|
60
66
|
args = args.map { |a| a.to_s.gsub(/^hari\:/, '') }
|
61
67
|
args.one? ? find_one(args[0], options) : find_many(args, options)
|
62
68
|
end
|
@@ -66,6 +72,8 @@ module Hari
|
|
66
72
|
end
|
67
73
|
|
68
74
|
def find_many(ids, options = {})
|
75
|
+
return [] if ids.empty?
|
76
|
+
|
69
77
|
Hari.redis.mget(ids).map &method(:from_json)
|
70
78
|
end
|
71
79
|
|
@@ -3,28 +3,61 @@ module Hari
|
|
3
3
|
module Serialization
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
+
included do
|
7
|
+
include ActiveModel::Validations
|
8
|
+
include ActiveModel::Dirty
|
9
|
+
extend Property::Builder
|
10
|
+
end
|
11
|
+
|
12
|
+
autoload :Array, 'hari/entity/serialization/array'
|
6
13
|
autoload :Boolean, 'hari/entity/serialization/boolean'
|
7
14
|
autoload :Date, 'hari/entity/serialization/date'
|
8
15
|
autoload :DateTime, 'hari/entity/serialization/datetime'
|
9
16
|
autoload :Float, 'hari/entity/serialization/float'
|
17
|
+
autoload :Hash, 'hari/entity/serialization/hash'
|
10
18
|
autoload :Integer, 'hari/entity/serialization/integer'
|
11
19
|
autoload :String, 'hari/entity/serialization/string'
|
12
20
|
autoload :Time, 'hari/entity/serialization/time'
|
13
21
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
22
|
+
def initialize(attrs = {})
|
23
|
+
return if attrs.blank?
|
24
|
+
|
25
|
+
attrs = attrs.with_indifferent_access
|
26
|
+
|
27
|
+
self.class.properties.each do |prop|
|
28
|
+
write_attribute(prop.name, attrs[prop.name]) if attrs.key?(prop.name)
|
17
29
|
end
|
30
|
+
end
|
18
31
|
|
19
|
-
|
32
|
+
def attributes
|
33
|
+
self.class.properties.inject({}) do |buffer, prop|
|
34
|
+
buffer.merge prop.name => send(prop.name)
|
35
|
+
end
|
20
36
|
end
|
21
37
|
|
22
|
-
|
38
|
+
alias :attribute :send
|
39
|
+
alias :read_attribute :send
|
40
|
+
alias :has_attribute? :respond_to?
|
41
|
+
alias :read_attribute_for_serialization :send
|
23
42
|
|
24
|
-
|
25
|
-
|
43
|
+
def write_attribute(name, value)
|
44
|
+
send "#{name}=", value
|
45
|
+
end
|
26
46
|
|
27
|
-
|
47
|
+
def to_hash
|
48
|
+
self.class.properties.inject({}) do |buffer, prop|
|
49
|
+
buffer.merge prop.name => prop.serialize(self)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_json
|
54
|
+
Yajl::Encoder.encode to_hash
|
55
|
+
end
|
56
|
+
|
57
|
+
module ClassMethods
|
58
|
+
|
59
|
+
def from_hash(source)
|
60
|
+
hash = source.inject({}) do |buffer, (key, value)|
|
28
61
|
if prop = properties.find { |p| p.name == key }
|
29
62
|
buffer[key] = prop.desserialize(value)
|
30
63
|
end
|
@@ -32,7 +65,18 @@ module Hari
|
|
32
65
|
buffer
|
33
66
|
end
|
34
67
|
|
35
|
-
new
|
68
|
+
new(hash).tap { |e| e.changed_attributes.clear }
|
69
|
+
end
|
70
|
+
|
71
|
+
def from_json(source)
|
72
|
+
return if source.blank?
|
73
|
+
|
74
|
+
case source
|
75
|
+
when ::String
|
76
|
+
from_hash Yajl::Parser.parse(source)
|
77
|
+
when ::Hash
|
78
|
+
from_hash source
|
79
|
+
end
|
36
80
|
end
|
37
81
|
|
38
82
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Serialization
|
4
|
+
module Array
|
5
|
+
|
6
|
+
def self.serialize(value, options = {})
|
7
|
+
Array value
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.desserialize(value, options = {})
|
11
|
+
Array value
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.method_missing(method, *args, &block)
|
15
|
+
::Array.send method, *args, &block
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Hari
|
2
|
+
class Entity
|
3
|
+
module Serialization
|
4
|
+
module Hash
|
5
|
+
|
6
|
+
def self.serialize(value, options = {})
|
7
|
+
if value.blank?
|
8
|
+
{}
|
9
|
+
elsif value.respond_to?(:to_hash)
|
10
|
+
value.to_hash
|
11
|
+
elsif value.respond_to?(:to_h)
|
12
|
+
value.to_h
|
13
|
+
elsif value.respond_to?(:marshal_dump)
|
14
|
+
value.marshal_dump
|
15
|
+
else
|
16
|
+
fail 'value not accepted as a Hash'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.desserialize(value, options = {})
|
21
|
+
value
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.method_missing(method, *args, &block)
|
25
|
+
::Hash.send method, *args, &block
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/hari/keys.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
module Hari
|
2
2
|
module Keys
|
3
|
+
|
4
|
+
TYPES = %w(string list hash set sorted_set)
|
5
|
+
|
3
6
|
autoload :Key, 'hari/keys/key'
|
7
|
+
autoload :Hash, 'hari/keys/hash'
|
4
8
|
autoload :List, 'hari/keys/list'
|
5
9
|
autoload :Set, 'hari/keys/set'
|
6
10
|
autoload :SortedSet, 'hari/keys/sorted_set'
|
7
11
|
autoload :String, 'hari/keys/string'
|
12
|
+
|
8
13
|
end
|
9
14
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Hari
|
2
|
+
module Keys
|
3
|
+
class Hash < Key
|
4
|
+
|
5
|
+
def hash(name = nil)
|
6
|
+
return super() unless name
|
7
|
+
|
8
|
+
@name = name
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def hash!(name)
|
13
|
+
@name = name
|
14
|
+
to_h
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_h
|
18
|
+
Hari.redis.hgetall key
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete(field)
|
22
|
+
Hari.redis.hdel key, field
|
23
|
+
end
|
24
|
+
|
25
|
+
def key?(field)
|
26
|
+
Hari.redis.hexists key, field
|
27
|
+
end
|
28
|
+
|
29
|
+
alias :has_key? :key?
|
30
|
+
alias :member? :key?
|
31
|
+
|
32
|
+
def keys
|
33
|
+
Hari.redis.hkeys key
|
34
|
+
end
|
35
|
+
|
36
|
+
def values
|
37
|
+
Hari.redis.hvals key
|
38
|
+
end
|
39
|
+
|
40
|
+
def values_at(*keys)
|
41
|
+
Hari.redis.hmget key, keys
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](field)
|
45
|
+
Hari.redis.hget key, field
|
46
|
+
end
|
47
|
+
|
48
|
+
def set(field, value)
|
49
|
+
Hari.redis.hset key, field, value
|
50
|
+
end
|
51
|
+
|
52
|
+
alias :[]= :set
|
53
|
+
|
54
|
+
def merge!(args = {})
|
55
|
+
Hari.redis.hmset key, args.to_a.flatten
|
56
|
+
end
|
57
|
+
|
58
|
+
def count
|
59
|
+
Hari.redis.hlen key
|
60
|
+
end
|
61
|
+
|
62
|
+
alias :size :count
|
63
|
+
alias :length :count
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|