redis_object 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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