redisabel 0.1.0

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 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: []