redis_object 0.5.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.
Files changed (49) hide show
  1. data/.coveralls.yml +1 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +8 -0
  5. data/README.markdown +179 -0
  6. data/Rakefile +10 -0
  7. data/lib/redis_object.rb +47 -0
  8. data/lib/redis_object/base.rb +408 -0
  9. data/lib/redis_object/collection.rb +388 -0
  10. data/lib/redis_object/defaults.rb +42 -0
  11. data/lib/redis_object/experimental/history.rb +49 -0
  12. data/lib/redis_object/ext/benchmark.rb +34 -0
  13. data/lib/redis_object/ext/cleaner.rb +14 -0
  14. data/lib/redis_object/ext/filters.rb +68 -0
  15. data/lib/redis_object/ext/script_cache.rb +92 -0
  16. data/lib/redis_object/ext/shardable.rb +18 -0
  17. data/lib/redis_object/ext/triggers.rb +101 -0
  18. data/lib/redis_object/ext/view_caching.rb +258 -0
  19. data/lib/redis_object/ext/views.rb +102 -0
  20. data/lib/redis_object/external_index.rb +25 -0
  21. data/lib/redis_object/indices.rb +97 -0
  22. data/lib/redis_object/inheritance_tracking.rb +23 -0
  23. data/lib/redis_object/keys.rb +37 -0
  24. data/lib/redis_object/storage.rb +93 -0
  25. data/lib/redis_object/storage/adapter.rb +46 -0
  26. data/lib/redis_object/storage/aws.rb +71 -0
  27. data/lib/redis_object/storage/mysql.rb +47 -0
  28. data/lib/redis_object/storage/redis.rb +119 -0
  29. data/lib/redis_object/timestamps.rb +74 -0
  30. data/lib/redis_object/tpl.rb +17 -0
  31. data/lib/redis_object/types.rb +276 -0
  32. data/lib/redis_object/validation.rb +89 -0
  33. data/lib/redis_object/version.rb +5 -0
  34. data/redis_object.gemspec +26 -0
  35. data/spec/adapter_spec.rb +43 -0
  36. data/spec/base_spec.rb +90 -0
  37. data/spec/benchmark_spec.rb +46 -0
  38. data/spec/collections_spec.rb +144 -0
  39. data/spec/defaults_spec.rb +56 -0
  40. data/spec/filters_spec.rb +29 -0
  41. data/spec/indices_spec.rb +45 -0
  42. data/spec/rename_class_spec.rb +96 -0
  43. data/spec/spec_helper.rb +38 -0
  44. data/spec/timestamp_spec.rb +28 -0
  45. data/spec/trigger_spec.rb +51 -0
  46. data/spec/types_spec.rb +103 -0
  47. data/spec/view_caching_spec.rb +130 -0
  48. data/spec/views_spec.rb +72 -0
  49. metadata +172 -0
@@ -0,0 +1,102 @@
1
+ module Seabright
2
+ module Views
3
+
4
+ ViewFieldGetter = "local out = {}
5
+ local key
6
+ local val
7
+ for i=1,#ARGV do
8
+ key = ARGV[i]
9
+ val = redis.call('HGET',KEYS[1],key)
10
+ if val then
11
+ table.insert(out,key)
12
+ table.insert(out,val)
13
+ end
14
+ end
15
+ return out".gsub(/\t/,'').freeze
16
+
17
+ def view_as_hash(name)
18
+ out = {}
19
+ if requested_set = self.class.named_views[name]
20
+ if requested_set.is_a?(Symbol) and self.respond_to?(requested_set)
21
+ out = send(requested_set)
22
+ else
23
+ methods = requested_set[:fields].select {|f| self.respond_to?(f.to_sym) }
24
+ if methods.count > 0
25
+ methods.each do |m|
26
+ out[m.to_s] = send(m.to_sym)
27
+ end
28
+ end
29
+ if requested_set[:fields] && (flds = requested_set[:fields].select {|f| !out.keys.include?(f.to_s) }.map {|f| f.to_s }) && flds.count > 0
30
+ res = Hash[*store.eval(ViewFieldGetter, [hkey], flds)]
31
+ out.merge!(res)
32
+ end
33
+ if requested_set[:procs]
34
+ requested_set[:procs].each do |k,proc|
35
+ out[k.to_s] = proc.call(self)
36
+ end
37
+ end
38
+ if requested_set[:hashes]
39
+ requested_set[:hashes].each do |k,v|
40
+ case v
41
+ when String, Symbol
42
+ out[k.to_s] = get(v)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ out
49
+ end
50
+
51
+ def view_as_json(name)
52
+ Yajl::Encoder.encode(view_as_hash(name))
53
+ end
54
+
55
+ module ClassMethods
56
+
57
+ def named_view(name,*fields)
58
+ named_views[name] = normalize_field_options(fields)
59
+ end
60
+
61
+ def named_views
62
+ @named_views ||= {}
63
+ end
64
+
65
+ def normalize_field_options(fields)
66
+ fields.flatten!
67
+ fields.uniq!
68
+
69
+ options = {}
70
+ if fields.last.is_a?(Hash) # assume an option hash
71
+ options.merge!(fields.slice!(fields.size - 1, 1)[0])
72
+ end
73
+
74
+ # assign a the method as a symbol to be exclusively invoked on view
75
+ # so instead of returning a hash on view, it will return only what was
76
+ # produced by calling the method.
77
+ if options.keys.size > 0 and options[:method]
78
+ out = options[:method].to_sym
79
+ else
80
+ hash = fields.select {|f| f.is_a?(Hash) }.inject({},:merge)
81
+ out = {}
82
+ if (h = hash.select {|k,v| !v.is_a?(Proc) }) && h.count > 0
83
+ out[:hashes] = h
84
+ end
85
+ if (h = hash.select {|k,v| v.is_a?(Proc) }) && h.count > 0
86
+ out[:procs] = h
87
+ end
88
+ if (h = fields.select {|o| o.is_a?(String) || o.is_a?(Symbol) }) && h.count > 0
89
+ out[:fields] = h
90
+ end
91
+ end
92
+ out
93
+ end
94
+
95
+ end
96
+
97
+ def self.included(base)
98
+ base.extend(ClassMethods)
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,25 @@
1
+ module Seabright
2
+ class ExternalIndex
3
+
4
+ class << self
5
+
6
+ def buildthisout
7
+ end
8
+
9
+ private
10
+
11
+ def redis
12
+ @@redis ||= Seabright::RedisPool.connection
13
+ end
14
+
15
+ end
16
+
17
+ private
18
+
19
+ def redis
20
+ @@redis ||= self.class.redis
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,97 @@
1
+ module Seabright
2
+ module Indices
3
+
4
+ def index_key(idx)
5
+ self.class.index_key(idx)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def intercept_sets_for_indices!
11
+ return if @intercepted_sets_for_indices
12
+ self.class_eval do
13
+ alias_method :unindexed_set, :set unless method_defined?(:unindexed_set)
14
+ def set(k,v)
15
+ ret = unindexed_set(k,v)
16
+ if self.class.has_sort_index?(k)
17
+ store.zrem(index_key(k), hkey)
18
+ store.zadd(index_key(k), score_format(k,v), hkey)
19
+ end
20
+ ret
21
+ end
22
+ alias_method :unindexed_mset, :mset unless method_defined?(:unindexed_mset)
23
+ def mset(dat)
24
+ ret = unindexed_mset(dat)
25
+ dat.select {|k,v| self.class.has_sort_index?(k) }.each do |k,v|
26
+ store.zrem(index_key(k), hkey)
27
+ store.zadd(index_key(k), score_format(k,v), hkey)
28
+ end
29
+ ret
30
+ end
31
+ alias_method :unindexed_setnx, :setnx unless method_defined?(:unindexed_setnx)
32
+ def setnx(k,v)
33
+ ret = unindexed_setnx(k,v)
34
+ if self.class.has_sort_index?(k)
35
+ store.zrem(index_key(k), hkey)
36
+ store.zadd(index_key(k), score_format(k,v), hkey)
37
+ end
38
+ ret
39
+ end
40
+
41
+ end
42
+ @intercepted_sets_for_indices = true
43
+ end
44
+
45
+ def indexed(idx,num=-1,reverse=false)
46
+ out = Enumerator.new do |yielder|
47
+ store.send(reverse ? :zrevrange : :zrange, index_key(idx), 0, num-1).each do |member|
48
+ if a = self.find_by_key(member)
49
+ yielder << a
50
+ end
51
+ end
52
+ end
53
+ if block_given?
54
+ out.each do |itm|
55
+ yield itm
56
+ end
57
+ else
58
+ out
59
+ end
60
+ end
61
+
62
+ def index_key(idx)
63
+ "#{self.plname}::#{idx}"
64
+ end
65
+
66
+ def sort_indices
67
+ @@sort_indices ||= []
68
+ end
69
+
70
+ def sort_by(k)
71
+ sort_indices << k.to_sym
72
+ intercept_sets_for_indices!
73
+ end
74
+
75
+ def reindex(k)
76
+ store.del index_key(k)
77
+ all.each do |obj|
78
+ obj.set(k,obj.get(k))
79
+ end
80
+ end
81
+
82
+ def has_sort_index?(k)
83
+ sort_indices.include?(k.to_sym)
84
+ end
85
+
86
+ def latest
87
+ indexed(:created_at,999,true).first
88
+ end
89
+
90
+ end
91
+
92
+ def self.included(base)
93
+ base.extend(ClassMethods)
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,23 @@
1
+ module Seabright
2
+ module InheritanceTracking
3
+
4
+ module ClassMethods
5
+ def inherited(child_class)
6
+ child_classes_set.add(child_class)
7
+ end
8
+
9
+ def child_classes_set
10
+ @child_classes_set ||= Set.new
11
+ end
12
+
13
+ def child_classes
14
+ child_classes_set.to_a
15
+ end
16
+ end
17
+
18
+ def self.included(base)
19
+ base.extend(ClassMethods)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module Seabright
2
+ module Keys
3
+
4
+ def key(ident = id)
5
+ "#{self.class.cname}:#{ident.gsub(/^.*:/,'')}"
6
+ end
7
+
8
+ def reserve_key(ident = id)
9
+ "#{key(ident)}_reserve"
10
+ end
11
+
12
+ def hkey(ident = nil)
13
+ "#{key}_h"
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ def key(ident=nil)
19
+ "#{cname}#{ident ? ":#{ident.gsub(/^.*:/,'')}" : ""}"
20
+ end
21
+
22
+ def reserve_key(ident=nil)
23
+ "#{key(ident)}_reserve"
24
+ end
25
+
26
+ def hkey(ident = nil)
27
+ "#{key(ident)}_h"
28
+ end
29
+
30
+ end
31
+
32
+ def self.included(base)
33
+ base.extend(ClassMethods)
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,93 @@
1
+ module Seabright
2
+ module Storage
3
+
4
+ require 'redis_object/storage/adapter'
5
+ autoload :Redis, 'redis_object/storage/redis'
6
+ autoload :MySQL, 'redis_object/storage/mysql'
7
+
8
+ def store
9
+ self.class.store
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def set_storage(adp=adapter)
15
+ @adapter = adp
16
+ end
17
+
18
+ def store(id=store_name)
19
+ adapters[id] ||= const_get(adapter).new(config(id))
20
+ end
21
+
22
+ def configure_store(conf,id=store_name,*ids)
23
+ configs[id] = conf
24
+ ids.each do |i|
25
+ configs[i] = conf
26
+ end
27
+ store(id)
28
+ end
29
+
30
+ def use_store(id)
31
+ raise "Cannot use non-existent store: #{id}" unless config(id)
32
+ @store_name = id.to_sym
33
+ end
34
+
35
+ def reconnect!
36
+ adapters.each do |k,v|
37
+ v.reconnect!
38
+ end
39
+ end
40
+
41
+ def adapters
42
+ @@adapters ||= {}
43
+ end
44
+
45
+ def adapter
46
+ @adapter ||= config[:adapter].to_sym || :Redis
47
+ end
48
+
49
+ def store_name
50
+ @store_name ||= :global
51
+ end
52
+
53
+ def configs
54
+ @@conf ||= {}
55
+ end
56
+
57
+ def config(id=store_name)
58
+ configs[id]
59
+ end
60
+
61
+ def stores
62
+ adapters
63
+ end
64
+
65
+ def dump_stores_to_files(path)
66
+ raise "Directory does not exist!" unless Dir.exists?(File.dirname(path))
67
+ adapters.each do |name,adptr|
68
+ if adptr.respond_to? :dump_to_file
69
+ puts "Dumping #{name} into #{path}/#{name.to_s}.dump"
70
+ adptr.dump_to_file("#{path}/#{name.to_s}.dump")
71
+ end
72
+ end
73
+ end
74
+
75
+ def restore_stores_from_files(path)
76
+ raise "Directory does not exist!" unless Dir.exists?(File.dirname(path))
77
+ Dir.glob(path + "/*.dump").each do |file|
78
+ name = file.gsub(/\.[^\.]+$/,'').gsub(/.*\//,'').to_sym
79
+ if (stor = store(name)) && stor.respond_to?(:restore_from_file)
80
+ puts "Restoring #{name} from #{file}"
81
+ stor.restore_from_file(file)
82
+ end
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ def self.included(base)
89
+ base.extend(ClassMethods)
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,46 @@
1
+ module Seabright
2
+ module Storage
3
+ class Adapter
4
+
5
+ def initialize(config={})
6
+ @config = config
7
+ end
8
+
9
+ def configure(conf)
10
+ @config = conf
11
+ reset
12
+ end
13
+
14
+ def config
15
+ @config ||= {}
16
+ end
17
+
18
+ def config_opts(*opts)
19
+ opts.inject({}) do |a,k|
20
+ a[k] = config[k]
21
+ a
22
+ end
23
+ end
24
+
25
+ def reset
26
+ connections.each_index do |i|
27
+ connections[i] = nil
28
+ end
29
+ end
30
+ alias_method :reconnect!, :reset
31
+
32
+ def connection(num=0)
33
+ connections[num] ||= new_connection
34
+ end
35
+
36
+ def connections
37
+ @connections ||= []
38
+ end
39
+
40
+ def new_connection
41
+ true
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,71 @@
1
+
2
+ module Seabright
3
+ module Storage
4
+ class AWS
5
+
6
+ def initialize(config={})
7
+ puts "Got config: '#{config.inspect}'"
8
+ @config = config
9
+ end
10
+
11
+ def configure(conf)
12
+ @config = conf
13
+ reset
14
+ end
15
+
16
+ def set
17
+
18
+ end
19
+
20
+ def sadd
21
+
22
+ end
23
+
24
+ def del
25
+
26
+ end
27
+
28
+ def srem
29
+
30
+ end
31
+
32
+ def smembers
33
+
34
+ end
35
+
36
+ def exists
37
+
38
+ end
39
+
40
+ def hget
41
+
42
+ end
43
+
44
+ def hset
45
+
46
+ end
47
+
48
+ private
49
+
50
+ def reset
51
+ @connection = nil
52
+ end
53
+
54
+ def connection
55
+ @connection ||= new_connection
56
+ end
57
+
58
+ def new_connection
59
+ require 'fog'
60
+ require 'aws-sdk'
61
+
62
+ opts = [:path, :db, :password].inject({}) {|a,k|
63
+ a[k] = @config[k]
64
+ a
65
+ }
66
+ ::Redis.new(opts)
67
+ end
68
+
69
+ end
70
+ end
71
+ end