chimera 0.0.1

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 (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