chimera 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +57 -0
  3. data/PostInstall.txt +7 -0
  4. data/README.rdoc +114 -0
  5. data/Rakefile +30 -0
  6. data/doc/NOTES +11 -0
  7. data/doc/examples/config.yml +16 -0
  8. data/doc/redis6379.conf +132 -0
  9. data/lib/chimera.rb +33 -0
  10. data/lib/chimera/associations.rb +146 -0
  11. data/lib/chimera/attributes.rb +52 -0
  12. data/lib/chimera/base.rb +95 -0
  13. data/lib/chimera/config.rb +9 -0
  14. data/lib/chimera/error.rb +12 -0
  15. data/lib/chimera/finders.rb +49 -0
  16. data/lib/chimera/geo_indexes.rb +76 -0
  17. data/lib/chimera/indexes.rb +177 -0
  18. data/lib/chimera/persistence.rb +70 -0
  19. data/lib/chimera/redis_objects.rb +345 -0
  20. data/lib/redis.rb +373 -0
  21. data/lib/redis/counter.rb +94 -0
  22. data/lib/redis/dist_redis.rb +149 -0
  23. data/lib/redis/hash_ring.rb +135 -0
  24. data/lib/redis/helpers/core_commands.rb +46 -0
  25. data/lib/redis/helpers/serialize.rb +25 -0
  26. data/lib/redis/list.rb +122 -0
  27. data/lib/redis/lock.rb +83 -0
  28. data/lib/redis/objects.rb +100 -0
  29. data/lib/redis/objects/counters.rb +132 -0
  30. data/lib/redis/objects/lists.rb +45 -0
  31. data/lib/redis/objects/locks.rb +71 -0
  32. data/lib/redis/objects/sets.rb +46 -0
  33. data/lib/redis/objects/values.rb +56 -0
  34. data/lib/redis/pipeline.rb +21 -0
  35. data/lib/redis/set.rb +156 -0
  36. data/lib/redis/value.rb +35 -0
  37. data/lib/riak_raw.rb +100 -0
  38. data/lib/typhoeus.rb +55 -0
  39. data/lib/typhoeus/.gitignore +1 -0
  40. data/lib/typhoeus/easy.rb +253 -0
  41. data/lib/typhoeus/filter.rb +28 -0
  42. data/lib/typhoeus/hydra.rb +210 -0
  43. data/lib/typhoeus/multi.rb +34 -0
  44. data/lib/typhoeus/remote.rb +306 -0
  45. data/lib/typhoeus/remote_method.rb +108 -0
  46. data/lib/typhoeus/remote_proxy_object.rb +48 -0
  47. data/lib/typhoeus/request.rb +124 -0
  48. data/lib/typhoeus/response.rb +39 -0
  49. data/lib/typhoeus/service.rb +20 -0
  50. data/script/console +10 -0
  51. data/script/destroy +14 -0
  52. data/script/generate +14 -0
  53. data/test/models.rb +49 -0
  54. data/test/test_chimera.rb +238 -0
  55. data/test/test_helper.rb +7 -0
  56. metadata +243 -0
@@ -0,0 +1,71 @@
1
+ # This is the class loader, for use as "include Redis::Objects::Locks"
2
+ # For the object itself, see "Redis::Lock"
3
+ require 'redis/lock'
4
+ class Redis
5
+ module Objects
6
+ class UndefinedLock < StandardError; end #:nodoc:
7
+ module Locks
8
+ def self.included(klass)
9
+ klass.send :include, InstanceMethods
10
+ klass.extend ClassMethods
11
+ end
12
+
13
+ # Class methods that appear in your class when you include Redis::Objects.
14
+ module ClassMethods
15
+ # Define a new lock. It will function like a model attribute,
16
+ # so it can be used alongside ActiveRecord/DataMapper, etc.
17
+ def lock(name, options={})
18
+ options[:timeout] ||= 5 # seconds
19
+ options[:init] = false if options[:init].nil? # default :init to false
20
+ @redis_objects[name] = options.merge(:type => :lock)
21
+ if options[:global]
22
+ instance_eval <<-EndMethods
23
+ def #{name}_lock(&block)
24
+ @#{name} ||= Redis::Lock.new(field_key(:#{name}_lock, ''), redis, @redis_objects[:#{name}])
25
+ end
26
+ EndMethods
27
+ class_eval <<-EndMethods
28
+ def #{name}_lock(&block)
29
+ self.class.#{name}(block)
30
+ end
31
+ EndMethods
32
+ else
33
+ class_eval <<-EndMethods
34
+ def #{name}_lock(&block)
35
+ raise(ActiveRedis::Errors::NotSavedError) if self.new?
36
+ @#{name} ||= Redis::Lock.new(field_key(:#{name}_lock), redis, self.class.redis_objects[:#{name}])
37
+ end
38
+ EndMethods
39
+ end
40
+
41
+
42
+
43
+ end
44
+
45
+ # Obtain a lock, and execute the block synchronously. Any other code
46
+ # (on any server) will spin waiting for the lock up to the :timeout
47
+ # that was specified when the lock was defined.
48
+ def obtain_lock(name, id, &block)
49
+ verify_lock_defined!(name)
50
+ raise ArgumentError, "Missing block to #{self.name}.obtain_lock" unless block_given?
51
+ lock_name = field_key("#{name}_lock", id)
52
+ Redis::Lock.new(lock_name, redis, self.redis_objects[name]).lock(&block)
53
+ end
54
+
55
+ # Clear the lock. Use with care - usually only in an Admin page to clear
56
+ # stale locks (a stale lock should only happen if a server crashes.)
57
+ def clear_lock(name, id)
58
+ verify_lock_defined!(name)
59
+ lock_name = field_key("#{name}_lock", id)
60
+ redis.del(lock_name)
61
+ end
62
+
63
+ private
64
+
65
+ def verify_lock_defined!(name)
66
+ raise Redis::Objects::UndefinedLock, "Undefined lock :#{name} for class #{self.name}" unless @redis_objects.has_key?(name)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,46 @@
1
+ # This is the class loader, for use as "include Redis::Objects::Sets"
2
+ # For the object itself, see "Redis::Set"
3
+ require 'redis/set'
4
+ class Redis
5
+ module Objects
6
+ module Sets
7
+ def self.included(klass)
8
+ klass.send :include, InstanceMethods
9
+ klass.extend ClassMethods
10
+ end
11
+
12
+ # Class methods that appear in your class when you include Redis::Objects.
13
+ module ClassMethods
14
+ # Define a new list. It will function like a regular instance
15
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
16
+ def set(name, options={})
17
+ @redis_objects[name] = options.merge(:type => :set)
18
+ if options[:global]
19
+ instance_eval <<-EndMethods
20
+ def #{name}
21
+ @#{name} ||= Redis::Set.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
22
+ end
23
+ EndMethods
24
+ class_eval <<-EndMethods
25
+ def #{name}
26
+ self.class.#{name}
27
+ end
28
+ EndMethods
29
+ else
30
+ class_eval <<-EndMethods
31
+ def #{name}
32
+ raise(ActiveRedis::Errors::NotSavedError) if self.new?
33
+ @#{name} ||= Redis::Set.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
34
+ end
35
+ EndMethods
36
+ end
37
+
38
+ end
39
+ end
40
+
41
+ # Instance methods that appear in your class when you include Redis::Objects.
42
+ module InstanceMethods
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,56 @@
1
+ # This is the class loader, for use as "include Redis::Objects::Values"
2
+ # For the object itself, see "Redis::Value"
3
+ require 'redis/value'
4
+ class Redis
5
+ module Objects
6
+ module Values
7
+ def self.included(klass)
8
+ klass.send :include, InstanceMethods
9
+ klass.extend ClassMethods
10
+ end
11
+
12
+ # Class methods that appear in your class when you include Redis::Objects.
13
+ module ClassMethods
14
+ # Define a new simple value. It will function like a regular instance
15
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
16
+ def value(name, options={})
17
+ @redis_objects[name] = options.merge(:type => :value)
18
+ if options[:global]
19
+ instance_eval <<-EndMethods
20
+ def #{name}
21
+ @#{name} ||= Redis::Value.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
22
+ end
23
+ def #{name}=(value)
24
+ #{name}.value = value
25
+ end
26
+ EndMethods
27
+ class_eval <<-EndMethods
28
+ def #{name}
29
+ self.class.#{name}
30
+ end
31
+ def #{name}=(value)
32
+ self.class.#{name} = value
33
+ end
34
+ EndMethods
35
+ else
36
+ class_eval <<-EndMethods
37
+ def #{name}
38
+ raise(ActiveRedis::Errors::NotSavedError) if self.new?
39
+ @#{name} ||= Redis::Value.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
40
+ end
41
+ def #{name}=(value)
42
+ raise(ActiveRedis::Errors::NotSavedError) if self.new?
43
+ #{name}.value = value
44
+ end
45
+ EndMethods
46
+ end
47
+
48
+ end
49
+ end
50
+
51
+ # Instance methods that appear in your class when you include Redis::Objects.
52
+ module InstanceMethods
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,21 @@
1
+ class Redis
2
+ class Pipeline < Redis
3
+ BUFFER_SIZE = 50_000
4
+
5
+ def initialize(redis)
6
+ @redis = redis
7
+ @commands = []
8
+ end
9
+
10
+ def call_command(command)
11
+ @commands << command
12
+ end
13
+
14
+ def execute
15
+ return if @commands.empty?
16
+ @redis.call_command(@commands)
17
+ @commands.clear
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,156 @@
1
+ class Redis
2
+ #
3
+ # Class representing a set.
4
+ #
5
+ class Set
6
+ require 'enumerator'
7
+ include Enumerable
8
+ require 'redis/helpers/core_commands'
9
+ include Redis::Helpers::CoreCommands
10
+ require 'redis/helpers/serialize'
11
+ include Redis::Helpers::Serialize
12
+
13
+ attr_reader :key, :options, :redis
14
+
15
+ # Create a new Set.
16
+ def initialize(key, redis=$redis, options={})
17
+ @key = key
18
+ @redis = redis
19
+ @options = options
20
+ end
21
+
22
+ # Works like add. Can chain together: list << 'a' << 'b'
23
+ def <<(value)
24
+ add(value)
25
+ self # for << 'a' << 'b'
26
+ end
27
+
28
+ # Add the specified value to the set only if it does not exist already.
29
+ # Redis: SADD
30
+ def add(value)
31
+ redis.sadd(key, to_redis(value))
32
+ end
33
+
34
+ # Return all members in the set. Redis: SMEMBERS
35
+ def members
36
+ from_redis redis.smembers(key)
37
+ end
38
+ alias_method :get, :members
39
+
40
+ # Returns true if the specified value is in the set. Redis: SISMEMBER
41
+ def member?(value)
42
+ redis.sismember(key, to_redis(value))
43
+ end
44
+ alias_method :include?, :member?
45
+
46
+ # Delete the value from the set. Redis: SREM
47
+ def delete(value)
48
+ redis.srem(key, value)
49
+ end
50
+
51
+ # Iterate through each member of the set. Redis::Objects mixes in Enumerable,
52
+ # so you can also use familiar methods like +collect+, +detect+, and so forth.
53
+ def each(&block)
54
+ members.each(&block)
55
+ end
56
+
57
+ # Return the intersection with another set. Can pass it either another set
58
+ # object or set name. Also available as & which is a bit cleaner:
59
+ #
60
+ # members_in_both = set1 & set2
61
+ #
62
+ # If you want to specify multiple sets, you must use +intersection+:
63
+ #
64
+ # members_in_all = set1.intersection(set2, set3, set4)
65
+ # members_in_all = set1.inter(set2, set3, set4) # alias
66
+ #
67
+ # Redis: SINTER
68
+ def intersection(*sets)
69
+ from_redis redis.sinter(key, *keys_from_objects(sets))
70
+ end
71
+ alias_method :intersect, :intersection
72
+ alias_method :inter, :intersection
73
+ alias_method :&, :intersection
74
+
75
+ # Calculate the intersection and store it in Redis as +name+. Returns the number
76
+ # of elements in the stored intersection. Redis: SUNIONSTORE
77
+ def interstore(name, *sets)
78
+ redis.sinterstore(name, key, *keys_from_objects(sets))
79
+ end
80
+
81
+ # Return the union with another set. Can pass it either another set
82
+ # object or set name. Also available as | and + which are a bit cleaner:
83
+ #
84
+ # members_in_either = set1 | set2
85
+ # members_in_either = set1 + set2
86
+ #
87
+ # If you want to specify multiple sets, you must use +union+:
88
+ #
89
+ # members_in_all = set1.union(set2, set3, set4)
90
+ #
91
+ # Redis: SUNION
92
+ def union(*sets)
93
+ from_redis redis.sunion(key, *keys_from_objects(sets))
94
+ end
95
+ alias_method :|, :union
96
+ alias_method :+, :union
97
+
98
+ # Calculate the union and store it in Redis as +name+. Returns the number
99
+ # of elements in the stored union. Redis: SUNIONSTORE
100
+ def unionstore(name, *sets)
101
+ redis.sunionstore(name, key, *keys_from_objects(sets))
102
+ end
103
+
104
+ # Return the difference vs another set. Can pass it either another set
105
+ # object or set name. Also available as ^ or - which is a bit cleaner:
106
+ #
107
+ # members_difference = set1 ^ set2
108
+ # members_difference = set1 - set2
109
+ #
110
+ # If you want to specify multiple sets, you must use +difference+:
111
+ #
112
+ # members_difference = set1.difference(set2, set3, set4)
113
+ # members_difference = set1.diff(set2, set3, set4)
114
+ #
115
+ # Redis: SDIFF
116
+ def difference(*sets)
117
+ from_redis redis.sdiff(key, *keys_from_objects(sets))
118
+ end
119
+ alias_method :diff, :difference
120
+ alias_method :^, :difference
121
+ alias_method :-, :difference
122
+
123
+ # Calculate the diff and store it in Redis as +name+. Returns the number
124
+ # of elements in the stored union. Redis: SDIFFSTORE
125
+ def diffstore(name, *sets)
126
+ redis.sdiffstore(name, key, *keys_from_objects(sets))
127
+ end
128
+
129
+ # The number of members in the set. Aliased as size. Redis: SCARD
130
+ def length
131
+ redis.scard(key)
132
+ end
133
+ alias_method :size, :length
134
+
135
+ # Returns true if the set has no members. Redis: SCARD == 0
136
+ def empty?
137
+ length == 0
138
+ end
139
+
140
+ def ==(x)
141
+ members == x
142
+ end
143
+
144
+ def to_s
145
+ members.join(', ')
146
+ end
147
+
148
+ private
149
+
150
+ def keys_from_objects(sets)
151
+ raise ArgumentError, "Must pass in one or more set names" if sets.empty?
152
+ sets.collect{|set| set.is_a?(Redis::Set) ? set.key : set}
153
+ end
154
+
155
+ end
156
+ end
@@ -0,0 +1,35 @@
1
+ class Redis
2
+ #
3
+ # Class representing a simple value. You can use standard Ruby operations on it.
4
+ #
5
+ class Value
6
+ require 'redis/helpers/core_commands'
7
+ include Redis::Helpers::CoreCommands
8
+ require 'redis/helpers/serialize'
9
+ include Redis::Helpers::Serialize
10
+
11
+ attr_reader :key, :options, :redis
12
+ def initialize(key, redis=$redis, options={})
13
+ @key = key
14
+ @redis = redis
15
+ @options = options
16
+ @redis.setnx(key, @options[:default]) if @options[:default]
17
+ end
18
+
19
+ def value=(val)
20
+ redis.set key, to_redis(val)
21
+ end
22
+ alias_method :set, :value=
23
+
24
+ def value
25
+ from_redis redis.get(key)
26
+ end
27
+ alias_method :get, :value
28
+
29
+ def to_s; value.to_s; end
30
+ alias_method :to_str, :to_s
31
+
32
+ def ==(x); value == x; end
33
+ def nil?; value.nil?; end
34
+ end
35
+ end
@@ -0,0 +1,100 @@
1
+ # gem "typhoeus", "= 0.1.18"
2
+ # gem "uuidtools", "= 2.1.1"
3
+ # gem "brianmario-yajl-ruby", "= 0.6.3"
4
+ # require "typhoeus"
5
+ # require "uuidtools"
6
+ # require "uri"
7
+ # require "yajl"
8
+
9
+ # A Ruby interface for the Riak (http://riak.basho.com/) key-value store.
10
+ #
11
+ # Example Usage:
12
+ #
13
+ # > client = RiakRaw::Client.new('127.0.0.1', 8098, 'raw')
14
+ # > client.delete('raw_example', 'doctestkey')
15
+ # > obj = client.store('raw_example', 'doctestkey', {'foo':2})
16
+ # > client.fetch('raw_example', 'doctestkey')
17
+ module RiakRaw
18
+ VERSION = '0.0.1'
19
+
20
+ class Client
21
+ attr_accessor :host, :port, :prefix, :client_id
22
+
23
+ def initialize(host="127.0.0.1", port=8098, prefix='riak', client_id=SecureRandom.base64)
24
+ @host = host
25
+ @port = port
26
+ @prefix = prefix
27
+ @client_id = client_id
28
+ end
29
+
30
+ def bucket(bucket_name,keys=false)
31
+ #request(:get, build_path(bucket_name))
32
+ response = request(:get,
33
+ build_path(bucket_name),
34
+ nil, nil,
35
+ {"returnbody" => "true", "keys" => keys})
36
+ if response.code == 200
37
+ if json = response.body
38
+ return Yajl::Parser.parse(json)
39
+ end
40
+ end; nil
41
+ end
42
+
43
+ def store(bucket_name, key, content, vclock=nil, links=[], content_type='application/json', w=2, dw=2, r=2)
44
+ headers = { 'Content-Type' => content_type,
45
+ 'X-Riak-ClientId' => self.client_id }
46
+ if vclock
47
+ headers['X-Riak-Vclock'] = vclock
48
+ end
49
+
50
+ response = request(:put,
51
+ build_path(bucket_name,key),
52
+ content, headers,
53
+ {"returnbody" => "false", "w" => w, "dw" => dw})
54
+
55
+ # returnbody=true could cause issues. instead we'll do a
56
+ # separate fetch. see: https://issues.basho.com/show_bug.cgi?id=52
57
+ if response.code == 204
58
+ response = fetch(bucket_name, key, r)
59
+ end
60
+
61
+ response
62
+ end
63
+
64
+ def fetch(bucket_name, key, r=2)
65
+ response = request(:get,
66
+ build_path(bucket_name, key),
67
+ nil, {}, {"r" => r})
68
+ end
69
+
70
+ # there could be concurrency issues if we don't force a short sleep
71
+ # after delete. see: https://issues.basho.com/show_bug.cgi?id=52
72
+ def delete(bucket_name, key, dw=2)
73
+ response = request(:delete,
74
+ build_path(bucket_name, key),
75
+ nil, {}, {"dw" => dw})
76
+ end
77
+
78
+ private
79
+
80
+ def build_path(bucket_name, key='')
81
+ "http://#{self.host}:#{self.port}/#{self.prefix}/#{URI.escape(bucket_name)}/#{URI.escape(key)}"
82
+ end
83
+
84
+ def request(method, uri, body="", headers={}, params={})
85
+ hydra = Typhoeus::Hydra.new
86
+ case method
87
+ when :get then
88
+ req = Typhoeus::Request.new(uri, :method => :get, :body => body, :headers => headers, :params => params)
89
+ when :post then
90
+ req = Typhoeus::Request.new(uri, :method => :post, :body => body, :headers => headers, :params => params)
91
+ when :put then
92
+ req = Typhoeus::Request.new(uri, :method => :put, :body => body, :headers => headers, :params => params)
93
+ when :delete then
94
+ req = Typhoeus::Request.new(uri, :method => :delete, :body => body, :headers => headers, :params => params)
95
+ end
96
+ hydra.queue(req); hydra.run
97
+ req.handled_response
98
+ end
99
+ end # Client
100
+ end