redisabel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c3c8c70acdee7a5f3241bcdea83c38da43ed4360
4
+ data.tar.gz: e8db271bab1fbab6bd989d9711557d1c122afee2
5
+ SHA512:
6
+ metadata.gz: e59e627086b94b466f55da6aa0123bf29b0f76a1616c6a2d7a8ed9979877afc491ef67d5746717c0012deda2928ac5d5c0e65d00c894dc2d8d1ca970a2e1d145
7
+ data.tar.gz: 0aee823341f55bba873ce6c6db3e64393ad99ce56341cffb93bef999bd4cef84cba31cfba313ebcdf0ed68bc156e975ce9d2089a61d10d02f5a98061428b716c
data/LICENSE.md ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2014, Matthias Geier
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,31 @@
1
+
2
+ module Redisabel
3
+ class Database
4
+
5
+ attr_reader :db
6
+ @@db = nil
7
+
8
+ private_class_method :new
9
+ def initialize(db=0)
10
+ @db = Redis.new(:db => db)
11
+ end
12
+
13
+ def self.create(db=0)
14
+ return if @@db
15
+ @@db = new(db)
16
+ end
17
+
18
+ def self.close
19
+ return unless @@db
20
+ @@db.disconnect
21
+ end
22
+
23
+ def self.db
24
+ return @@db.db if @@db
25
+ end
26
+
27
+ def self.ok
28
+ return "OK"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,55 @@
1
+ # = String class extension
2
+ #
3
+ # A string that is supposed to becode a Module, Class or similar can be
4
+ # transformed by using #constantize
5
+ #
6
+ # "Array".constantize
7
+ # => Array
8
+ #
9
+ # When writing file names in ruby, they are usually an underscore (snakecase)
10
+ # representation of the class name. It can be transformed with #camelize and
11
+ # in place with #camelize!
12
+ #
13
+ # "file_reader".camelize
14
+ # => "FileReader"
15
+ #
16
+ # s = "file_reader"
17
+ # s.camelize!
18
+ # s
19
+ # => "FileReader"
20
+ #
21
+ # The backwards transformation from a class name to snakecase is done with
22
+ # #underscore and in place with #underscore!
23
+ #
24
+ # "FileReader".underscore
25
+ # => "file_reader"
26
+ #
27
+ # s = "FileReader".underscore
28
+ # s.underscore!
29
+ # s
30
+ # => "file_reader"
31
+ #
32
+ #
33
+ #
34
+ class String
35
+ def constantize
36
+ return self.to_s.split('::').reduce(Module){ |m, c| m.const_get(c) }
37
+ end
38
+
39
+ def camelize
40
+ return self.to_s.split(/_/).map(&:capitalize).join
41
+ end
42
+
43
+ def camelize!
44
+ self.replace(self.to_s.camelize)
45
+ end
46
+
47
+ def underscore
48
+ return self.to_s.split(/([A-Z]?[^A-Z]*)/).reject(&:empty?).
49
+ map(&:downcase).join('_')
50
+ end
51
+
52
+ def underscore!
53
+ self.replace(self.to_s.underscore)
54
+ end
55
+ end
@@ -0,0 +1,19 @@
1
+
2
+ module Redisabel
3
+ module Finders
4
+ def find(id, asave=false)
5
+ key = "#{self.database_key_name}:#{id}"
6
+ return unless Database.db.exists(key)
7
+ return self.new(asave, id, transform(key))
8
+ end
9
+
10
+ def filter(pattern, asave=false)
11
+ filter_term = "#{self.database_key_name}:#{pattern}"
12
+ keys = Database.db.keys(filter_term)
13
+ return keys.map do |key|
14
+ id = key.gsub("#{self.database_key_name}:", '')
15
+ self.new(asave, id, transform(key))
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,53 @@
1
+
2
+ module Redisabel
3
+ module HashFunctions
4
+
5
+ def key_valid?(key)
6
+ return key.is_a?(Numeric)
7
+ end
8
+ protected :key_valid?
9
+
10
+ def [](key)
11
+ return @data[key]
12
+ end
13
+
14
+ def update_data(*data)
15
+ data = data.first.is_a?(Hash) ? data.first : {}
16
+ unless data.is_a?(self.class.data_type)
17
+ raise ArgumentError.new("update_data expects a #{self.class.data_type}")
18
+ end
19
+
20
+ data.each{ |k, v| self.store(k, v) }
21
+ end
22
+
23
+ def delete(key)
24
+ if (value = @data.delete(key)) && self.autosave? && !self.id.to_s.empty?
25
+ Database.db.send(self.class.redis_delete_method, self.database_key,
26
+ value)
27
+ end
28
+ end
29
+
30
+ def store(key, value)
31
+ @data.delete(key)
32
+ @data.store(key, value.to_s)
33
+ if self.autosave? && !self.id.to_s.empty?
34
+ Database.db.send(self.class.redis_store_method, self.database_key, key,
35
+ value.to_s)
36
+ end
37
+ end
38
+ alias_method :[]=, :store
39
+
40
+ def to_hash
41
+ return @data
42
+ end
43
+
44
+ def to_h
45
+ return @data.dup
46
+ end
47
+
48
+ def to_ary
49
+ return @data.values.flatten
50
+ end
51
+ alias_method :to_a, :to_ary
52
+ end
53
+ end
@@ -0,0 +1,58 @@
1
+
2
+ module Redisabel
3
+ class KeyArray < KeyValue
4
+
5
+ def self.data_type
6
+ return Array
7
+ end
8
+
9
+ def [](i)
10
+ return @data[i]
11
+ end
12
+
13
+ def delete(value)
14
+ if @data.delete(value) && self.autosave? && !self.id.to_s.empty?
15
+ Database.db.lrem(self.database_key, 0, value)
16
+ end
17
+ end
18
+
19
+ def delete_at(i)
20
+ if @data.delete_at(i) && self.autosave? && !self.id.to_s.empty?
21
+ tmp_value = Time.now.to_s
22
+ self.insert(i, tmp_value)
23
+ Database.db.lrem(self.database_key, 0, tmp_value)
24
+ end
25
+ end
26
+
27
+ def insert(i, value)
28
+ params = i.nil? ? [value] : [i, value]
29
+ amethod, lmethod = i.nil? ? [:push, :rpush] : [:insert, :lset]
30
+ @data.send(amethod, *params)
31
+ if self.autosave? && !self.id.to_s.empty?
32
+ Database.db.send(lmethod, self.database_key, *params)
33
+ end
34
+ end
35
+ alias_method :[]=, :insert
36
+
37
+ def push(*values)
38
+ values = values.shift if values.first.is_a?(Array)
39
+ values.each{ |v| self.insert(nil, v) }
40
+ end
41
+
42
+ def save
43
+ return super do |key|
44
+ Database.db.del(key)
45
+ results = @data.map{ |v| Database.db.rpush(key, v.to_s) }
46
+ next results.all?
47
+ end
48
+ end
49
+
50
+ def to_ary
51
+ return @data
52
+ end
53
+
54
+ def to_a
55
+ return @data.dup
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,25 @@
1
+
2
+ module Redisabel
3
+ class KeyHash < KeyValue
4
+ include HashFunctions
5
+
6
+ def self.data_type
7
+ return Hash
8
+ end
9
+
10
+ def self.redis_store_method
11
+ return :hset
12
+ end
13
+
14
+ def self.redis_delete_method
15
+ return :hdel
16
+ end
17
+
18
+ def save
19
+ return super do |key|
20
+ key_values = @data.to_a.flatten
21
+ next Database.db.hmset(key, *key_values) == Database::ok
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+
2
+ module Redisabel
3
+ class KeyOrderedSet < KeyValue
4
+ include HashFunctions
5
+
6
+ def self.data_type
7
+ return Hash
8
+ end
9
+
10
+ def self.redis_store_method
11
+ return :zadd
12
+ end
13
+
14
+ def self.redis_delete_method
15
+ return :zrem
16
+ end
17
+
18
+ def self.range(id, first, last)
19
+ key = "#{self.database_key_name}:#{id}"
20
+ return unless Database.db.exists(key)
21
+ data = transform_zset(key, :zrangebyscore, first, last)
22
+ return self.new(false, id, data).freeze
23
+ end
24
+
25
+ def value=(val)
26
+ if !val.respond_to?(:keys) || val.keys.any?{ |k| !self.key_valid?(k) }
27
+ raise ArgumentError.new('ordered sets only accept numbers as Hash keys')
28
+ end
29
+ super
30
+ end
31
+
32
+ def save
33
+ return super do |key|
34
+ Database.db.del(key)
35
+ results = @data.map do |k, v|
36
+ Database.db.zadd(key, k, v)
37
+ end
38
+ next results.all?
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+
2
+ module Redisabel
3
+ class KeySet < KeyValue
4
+ def self.data_type
5
+ return Array
6
+ end
7
+
8
+ def update_data(*data)
9
+ return super(data.flatten.uniq)
10
+ end
11
+ protected :update_data
12
+
13
+ def save
14
+ return super do |key|
15
+ Database.db.del(key)
16
+ results = @data.map{ |v| Database.db.sadd(key, v) }
17
+ next results.all?
18
+ end
19
+ end
20
+
21
+ def delete(value)
22
+ if @data.delete(value) && self.autosave? && !self.id.to_s.empty?
23
+ Database.db.srem(self.database_key, value)
24
+ end
25
+ end
26
+
27
+ def push(*values)
28
+ values = values.shift if values.first.is_a?(Array)
29
+ values.uniq!
30
+ @data += values - @data
31
+ if self.autosave? && !self.id.to_s.empty?
32
+ values.each{ |v| Database.db.sadd(self.database_key, v) }
33
+ end
34
+ end
35
+
36
+ def to_ary
37
+ return @data.dup
38
+ end
39
+ alias_method :to_a, :to_ary
40
+
41
+ def ==(other)
42
+ return (other.is_a?(KeyValue) && self.id == other.id &&
43
+ @data.sort == other.value.sort)
44
+ end
45
+
46
+ def value=(val)
47
+ super
48
+ @data.uniq!
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,112 @@
1
+
2
+ module Redisabel
3
+ class KeyValue
4
+ extend Transformations
5
+ extend Finders
6
+
7
+ class << self; public :transform end
8
+ attr_accessor :id
9
+
10
+ def self.data_type
11
+ return String
12
+ end
13
+
14
+ def self.database_key_name
15
+ return self.name.underscore
16
+ end
17
+
18
+ def initialize(asave=false, id='', *data)
19
+ @autosave = asave
20
+ @data = self.class.data_type.new
21
+ self.id = id || ''
22
+ self.update_data(*data)
23
+ end
24
+
25
+ def update_data(*data)
26
+ if data.first.is_a?(self.class.data_type)
27
+ data = data.shift
28
+ elsif !data.is_a?(self.class.data_type)
29
+ data = self.class.data_type.new
30
+ end
31
+ unless data.is_a?(self.class.data_type)
32
+ raise ArgumentError.new("update_data expects a #{self.class.data_type}")
33
+ end
34
+ @data = data
35
+ self.autosave
36
+ end
37
+ protected :update_data
38
+
39
+ def empty?
40
+ return @data.empty?
41
+ end
42
+
43
+ def inspect
44
+ return "#{self.class.name}:#{self.id} #{@data.inspect}"
45
+ end
46
+
47
+ def to_s
48
+ return "#{self.class.name}:#{self.id}"
49
+ end
50
+ alias_method :to_str, :to_s
51
+
52
+ def eql?(other)
53
+ return self == other
54
+ end
55
+
56
+ def ==(other)
57
+ return (other.is_a?(KeyValue) && self.id == other.id &&
58
+ @data == other.value)
59
+ end
60
+
61
+ def save
62
+ return false if self.id.to_s.empty? || self.frozen?
63
+ key = self.database_key
64
+ if block_given?
65
+ return yield(key)
66
+ else
67
+ return Database.db.set(key, @data) == Database::ok
68
+ end
69
+ end
70
+
71
+ def destroy
72
+ key = self.database_key
73
+ return Database.db.del(key) > 0
74
+ end
75
+
76
+ def load
77
+ key = self.database_key
78
+ data = self.class.transform(key)
79
+ @data = data
80
+ end
81
+
82
+ def database_key
83
+ return "#{self.class.database_key_name}:#{self.id}"
84
+ end
85
+ protected :database_key
86
+
87
+ def autosave
88
+ self.save if @autosave
89
+ end
90
+ protected :autosave
91
+
92
+ def autosave?
93
+ return (!self.frozen? && @autosave)
94
+ end
95
+
96
+ def autosave=(bool)
97
+ @autosave = bool
98
+ self.save if self.autosave?
99
+ end
100
+
101
+ def value
102
+ return @data.dup
103
+ end
104
+
105
+ def value=(val)
106
+ unless val.is_a?(self.class.data_type)
107
+ val = self.class.data_type.new
108
+ end
109
+ @data = val
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,31 @@
1
+
2
+ module Redisabel
3
+ class Search
4
+ def initialize(pattern='*', asave=false)
5
+ @pattern = pattern
6
+ @autosave = asave
7
+ end
8
+
9
+ def keys
10
+ @keys ||= Database.db.keys(@pattern)
11
+ end
12
+
13
+ def objects
14
+ self.keys
15
+ @objects ||= @keys.map do |key|
16
+ object_name, id = key.split(':')
17
+ next object_name.camelize.constantize.new(@autosave, id,
18
+ Transformations.transform(key))
19
+ end
20
+ end
21
+
22
+ def objects_by_type
23
+ self.objects
24
+ @objects_by_type ||= @objects.reduce({}) do |acc, obj|
25
+ acc[obj.class] ||= []
26
+ acc[obj.class] << obj
27
+ next acc
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,48 @@
1
+
2
+ module Redisabel
3
+ module Transformations
4
+ def detect_type(key)
5
+ return Database.db.type(key)
6
+ end
7
+ module_function :detect_type
8
+
9
+ def transform(key)
10
+ type = detect_type(key)
11
+ return self.send("transform_#{type}", key)
12
+ end
13
+ module_function :transform
14
+
15
+ def transform_hash(key)
16
+ db = Database.db
17
+ keys = db.hkeys(key)
18
+ values = db.hvals(key)
19
+ return keys.zip(values).to_h
20
+ end
21
+ module_function :transform_hash
22
+
23
+ def transform_list(key)
24
+ return Database.db.lrange(key, 0, -1)
25
+ end
26
+ module_function :transform_list
27
+
28
+ def transform_string(key)
29
+ return Database.db.get(key)
30
+ end
31
+ module_function :transform_string
32
+
33
+ def transform_set(key)
34
+ return Database.db.smembers(key)
35
+ end
36
+ module_function :transform_set
37
+
38
+ def transform_zset(key, method=:zrange, first=0, last=-1)
39
+ return Database.db.send(method, key, first, last, :withscores => true).
40
+ reduce({}) do |acc, (v, k)|
41
+
42
+ acc[k.to_i] = v
43
+ acc
44
+ end
45
+ end
46
+ module_function :transform_zset
47
+ end
48
+ end
data/lib/redisabel.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'redisabel/extensions/string'
2
+ require 'redisabel/database'
3
+ require 'redisabel/transformations'
4
+ require 'redisabel/finders'
5
+ require 'redisabel/hash_functions'
6
+ require 'redisabel/search'
7
+ require 'redisabel/key_value'
8
+ require 'redisabel/key_hash'
9
+ require 'redisabel/key_set'
10
+ require 'redisabel/key_ordered_set'
11
+ require 'redisabel/key_array'
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redisabel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthias Geier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.1'
27
+ description:
28
+ email:
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - LICENSE.md
34
+ - lib/redisabel.rb
35
+ - lib/redisabel/database.rb
36
+ - lib/redisabel/extensions/string.rb
37
+ - lib/redisabel/finders.rb
38
+ - lib/redisabel/hash_functions.rb
39
+ - lib/redisabel/key_array.rb
40
+ - lib/redisabel/key_hash.rb
41
+ - lib/redisabel/key_ordered_set.rb
42
+ - lib/redisabel/key_set.rb
43
+ - lib/redisabel/key_value.rb
44
+ - lib/redisabel/search.rb
45
+ - lib/redisabel/transformations.rb
46
+ homepage: https://github.com/matthias-geier/redisabel
47
+ licenses:
48
+ - BSD-2
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 2.1.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 2.2.2
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: A minimal object mapper for Redis
70
+ test_files: []