redpear 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/redpear.rb ADDED
@@ -0,0 +1,23 @@
1
+ require "nest"
2
+ require "redis"
3
+
4
+ module Redpear
5
+
6
+ def self.autoload(const, path = nil)
7
+ path ||= "redpear/#{const.to_s.downcase}"
8
+ super const, path
9
+ end
10
+
11
+ autoload :Column
12
+ autoload :Concern
13
+ autoload :Connection
14
+ autoload :Expiration
15
+ autoload :Finders
16
+ autoload :Index
17
+ autoload :Model
18
+ autoload :Namespace
19
+ autoload :Nest
20
+ autoload :Persistence
21
+ autoload :Schema
22
+
23
+ end
@@ -0,0 +1,53 @@
1
+ class Redpear::Column < String
2
+ attr_reader :type, :model
3
+
4
+ # Creates a new column.
5
+ # @param [Redpear::Model] model the model the column is associated with
6
+ # @param [String] name the column name
7
+ # @param [Symbol] type the column type (:string (default), :counter, :integer, :timestamp)
8
+ def initialize(model, name, type = nil)
9
+ super name.to_s
10
+ @model = model
11
+ @type = type.to_sym if type
12
+ end
13
+
14
+ # Casts a value to a type
15
+ #
16
+ # @param value the value to cast
17
+ # @param [Symbol, #optional] type the type to cast to, defaults to the column type
18
+ # @return the casted value
19
+ def type_cast(value, type = self.type)
20
+ case type
21
+ when :counter
22
+ value.to_i
23
+ when :integer
24
+ Kernel::Integer(value) rescue nil if value
25
+ when :timestamp
26
+ value = type_cast(value, :integer)
27
+ Time.at(value) if value
28
+ else
29
+ value
30
+ end
31
+ end
32
+
33
+ # @return [String] the column name
34
+ def name
35
+ to_s
36
+ end
37
+
38
+ # @return [Boolean] true if the column is readable
39
+ def readable?
40
+ true
41
+ end
42
+
43
+ # @return [Boolean] true if the column is writable
44
+ def writable?
45
+ type != :counter
46
+ end
47
+
48
+ # @return [Boolean] true if the column is an index
49
+ def index?
50
+ is_a? Redpear::Index
51
+ end
52
+
53
+ end
@@ -0,0 +1,10 @@
1
+ module Redpear::Concern
2
+
3
+ # Partially borrowed from ActiveSupport
4
+ def append_features(base)
5
+ return false if base < self
6
+ super
7
+ base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
8
+ end
9
+
10
+ end
@@ -0,0 +1,17 @@
1
+ module Redpear::Connection
2
+ extend Redpear::Concern
3
+
4
+ module ClassMethods
5
+
6
+ # @return [Redis] the current connection
7
+ def connection
8
+ @connection ||= (superclass.respond_to?(:connection) ? superclass.connection : Redis.current)
9
+ end
10
+
11
+ # @param [Redis] the connection to assign
12
+ def connection=(value)
13
+ @connection = value
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # Hash stringify_keys extension. "Borrowed" from ActiveSupport.
2
+ class Hash
3
+
4
+ # Return a new hash with all keys converted to strings.
5
+ def stringify_keys
6
+ dup.stringify_keys!
7
+ end
8
+
9
+ # Destructively convert all keys to strings.
10
+ def stringify_keys!
11
+ keys.each do |key|
12
+ self[key.to_s] = delete(key) unless key.is_a?(String)
13
+ end
14
+ self
15
+ end
16
+
17
+ end unless Hash.new.respond_to?(:symbolize_keys)
@@ -0,0 +1,27 @@
1
+ module Redpear::Expiration
2
+
3
+ # Expires the record.
4
+ # @param [Time, Integer] either a Time or an Integer period (in seconds)
5
+ def expire(value)
6
+ return false unless persisted?
7
+
8
+ case value
9
+ when Time
10
+ nest.expireat(value.to_i)
11
+ when Integer
12
+ nest.expire(value)
13
+ when String
14
+ value = Kernel::Integer(value) rescue nil
15
+ expire(value)
16
+ else
17
+ false
18
+ end
19
+ end
20
+
21
+ # @return [Integer] the period this record has to live.
22
+ # May return -1 for non-expiring records and nil for non-persisted records.
23
+ def ttl
24
+ nest.ttl if persisted?
25
+ end
26
+
27
+ end
@@ -0,0 +1,51 @@
1
+ module Redpear::Finders
2
+ extend Redpear::Concern
3
+
4
+ module ClassMethods
5
+
6
+ # @return [Array] the IDs of all existing records
7
+ def members
8
+ mb_nest.smembers
9
+ end
10
+
11
+ # @return [Integer] the number of total records
12
+ def count
13
+ members.size
14
+ end
15
+
16
+ # @return [Array] all records
17
+ def all
18
+ members.map {|id| find(id) }
19
+ end
20
+
21
+ # Finds a single record.
22
+ #
23
+ # @param id the ID of the record to retrieve
24
+ # @param [Hash] options additional options
25
+ # @option :lazy defaults to true, set to false to load the record instantly
26
+ # @return [Redpear::Model] a record, or nil when not found
27
+ def find(id, options = {})
28
+ record = instantiate('id' => id.to_s) # Initialize
29
+ if record.nest.exists # Do we have a record key?
30
+ record.refresh_attributes if options[:lazy] == false
31
+ record
32
+ else # Must be an expired or orphaned one
33
+ record.destroy # Destroy (removes from mb_nest set)
34
+ nil
35
+ end
36
+ end
37
+
38
+ # @param id the ID to check
39
+ # @return [Boolean] true or false
40
+ def exists?(id)
41
+ mb_nest.sismember(id)
42
+ end
43
+
44
+ def instantiate(*a)
45
+ new(*a).tap do |instance|
46
+ instance.send :instance_variable_set, :@__loaded__, false
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,24 @@
1
+ class Redpear::Index < Redpear::Column
2
+
3
+ # @return [Redpear::Nest] the namespace of the index. Example:
4
+ #
5
+ # index = Comment.columns.lookup["post_id"]
6
+ # index.namespace # => "comments:post_id"
7
+ #
8
+ def namespace
9
+ model.namespace[self]
10
+ end
11
+
12
+ # @return [Redpear::Nest] the nest for a specific value. Example:
13
+ #
14
+ # index = Comment.columns.lookup["post_id"]
15
+ # index.nest(123) # => "comments:post_id:123"
16
+ # index.nest(nil) # => nil
17
+ # index.nest("") # => nil
18
+ #
19
+ def nest(value)
20
+ return nil if value.nil? || (value.respond_to?(:empty?) && value.empty?)
21
+ namespace[value]
22
+ end
23
+
24
+ end
@@ -0,0 +1,50 @@
1
+ require 'machinist'
2
+
3
+ # Machinist module for your tests/specs. Example:
4
+ #
5
+ # # spec/support/blueprints.rb
6
+ # require "redpear/machinist"
7
+ #
8
+ # Post.blueprint do
9
+ # title { "A Title" }
10
+ # created_at { 2.days.ago }
11
+ # end
12
+ #
13
+ module Redpear::Machinist
14
+
15
+ class Blueprint < Machinist::Blueprint
16
+
17
+ def make!(attributes = {})
18
+ make(attributes).tap &:save
19
+ end
20
+
21
+ def lathe_class #:nodoc:
22
+ Lathe
23
+ end
24
+
25
+ end
26
+
27
+ class Lathe < Machinist::Lathe
28
+ protected
29
+
30
+ def make_one_value(attribute, args)
31
+ return unless block_given?
32
+ raise_argument_error(attribute) unless args.empty?
33
+ yield
34
+ end
35
+
36
+ def assign_attribute(key, value) #:nodoc:
37
+ @assigned_attributes[key.to_sym] = value
38
+ @object.load key => value
39
+ end
40
+
41
+ end
42
+ end
43
+
44
+ class Redpear::Model #:nodoc:
45
+ extend Machinist::Machinable
46
+
47
+ def self.blueprint_class
48
+ Redpear::Machinist::Blueprint
49
+ end
50
+ end
@@ -0,0 +1,109 @@
1
+ require "set"
2
+ require "redpear/core_ext/stringify_keys"
3
+
4
+ =begin
5
+ Redis is a simple key/value store, hence storing structured data can be a
6
+ challenge. Redpear allows you to store/find/associate "records" in a Redis DB
7
+ very efficiently, minimising IO operations and storage space where possible.
8
+
9
+ For example:
10
+
11
+ class Post < Redpear::Model
12
+ column :title
13
+ column :body
14
+ end
15
+
16
+ class Comment < Redpear::Model
17
+ column :body
18
+ index :post_id
19
+ end
20
+
21
+ Let's create a post and a comment:
22
+
23
+ post = Post.save :title => "Hi!", :body => "I'm a new post"
24
+ comment = Comment.save :post_id => post.id, :body => "I like this!"
25
+
26
+ Redpear is VERY lightweight. Compared with other ORMs, it offers raw speed at
27
+ the expense of convenience.
28
+ =end
29
+ class Redpear::Model < Hash
30
+ include Redpear::Connection
31
+ include Redpear::Namespace
32
+ include Redpear::Persistence
33
+ include Redpear::Expiration
34
+ include Redpear::Schema
35
+ include Redpear::Finders
36
+
37
+ # Ensure we can read raw level values
38
+ alias_method :__fetch__, :[]
39
+
40
+ def initialize(attrs = {})
41
+ super()
42
+ @__attributes__ = {}
43
+ @__loaded__ = true
44
+ update(attrs)
45
+ end
46
+
47
+ # Every record needs an ID
48
+ def id
49
+ value = __fetch__("id")
50
+ value.to_s if value
51
+ end
52
+
53
+ # Custom comparator
54
+ def ==(other)
55
+ case other
56
+ when Redpear::Model
57
+ other.instance_of?(self.class) && to_hash(true) == other.to_hash(true)
58
+ else
59
+ super
60
+ end
61
+ end
62
+
63
+ # Attribute reader with type-casting
64
+ def [](name)
65
+ __ensure_loaded__
66
+ name = name.to_s
67
+ @__attributes__[name] ||= begin
68
+ column = self.class.columns.lookup[name]
69
+ value = super(name)
70
+ column ? column.type_cast(value) : value
71
+ end
72
+ end
73
+
74
+ # Attribute writer
75
+ def []=(name, value)
76
+ __ensure_loaded__
77
+ name = name.to_s
78
+ @__attributes__.delete(name)
79
+ super
80
+ end
81
+
82
+ # Returns a Hash with attributes
83
+ def to_hash(clean = false)
84
+ __ensure_loaded__
85
+ attrs = clean ? reject {|_, v| v.nil? } : self
86
+ {}.update(attrs)
87
+ end
88
+
89
+ # Show information about this record
90
+ def inspect
91
+ __ensure_loaded__
92
+ "#<#{self.class.name} #{super}>"
93
+ end
94
+
95
+ # Bulk-update attributes
96
+ def update(attrs)
97
+ attrs = (attrs ? attrs.stringify_keys : {})
98
+ attrs["id"] = attrs["id"].to_s if attrs["id"]
99
+ super
100
+ end
101
+ alias_method :load, :update
102
+
103
+ private
104
+
105
+ def __ensure_loaded__
106
+ refresh_attributes unless @__loaded__
107
+ end
108
+
109
+ end
@@ -0,0 +1,79 @@
1
+ # Namespace organization for models. Example:
2
+ #
3
+ # class Comment < Model
4
+ # index :post_id
5
+ # end
6
+ # instance = Comment.save(:post_id => 2)
7
+ #
8
+ # Comment.connection.keys
9
+ # # => ['comments:1', 'comments:+', 'comments:*', 'comments:post_id:2']
10
+ #
11
+ # # Instance nesting
12
+ # instance.nest # => 'comments:1'
13
+ # instance.nest.mapped_hmget_all # => { "post_id" => "2" }
14
+ #
15
+ # # Member nesting
16
+ # Comment.mb_nest # "comments:*"
17
+ # Comment.mb_nest.smembers # => #<Set: {1}>
18
+ #
19
+ # # PK nesting
20
+ # Comment.pk_nest # "comments:+"
21
+ # Comment.pk_nest.get # 1 = last ID
22
+ #
23
+ # # Index nesting
24
+ # Comment.columns["post_id"].nest(2) # "comments:post_id:2"
25
+ # Comment.columns["post_id"].nest(2).smembers # #<Set: {1}>
26
+ #
27
+ module Redpear::Namespace
28
+ extend Redpear::Concern
29
+
30
+ module ClassMethods
31
+
32
+ # @return [Redpear::Nest] the namespace of this model, Example:
33
+ #
34
+ # Comment.namespace # => "comments":Nest
35
+ #
36
+ def namespace
37
+ @namespace ||= Redpear::Nest.new(scope, connection)
38
+ end
39
+
40
+ # @return [String] the scope of this model. Example:
41
+ #
42
+ # Comment.scope # => "comments"
43
+ #
44
+ # Override if you want to use a differnet scope schema.
45
+ def scope
46
+ @scope ||= "#{name.split('::').last.downcase}s"
47
+ end
48
+
49
+ # @return [Redpear::Nest] the nest for the members store. Example:
50
+ #
51
+ # Comment.mb_nest # => 'comments:*'
52
+ # Comment.mb_nest.smembers # => [1, 2, 3]
53
+ #
54
+ def mb_nest
55
+ @mb_nest ||= namespace["*"]
56
+ end
57
+
58
+ # @return [Redpear::Nest] the nest for the primary-key incrementor. Example:
59
+ #
60
+ # Comment.pk_nest # => 'comments:+'
61
+ # Comment.pk_nest.get # => 0
62
+ # Comment.pk_nest.incr # => 1
63
+ #
64
+ def pk_nest
65
+ @pk_nest ||= namespace["+"]
66
+ end
67
+
68
+ end
69
+
70
+ # @return [Redpear::Nest] the nest for the current record. Example:
71
+ #
72
+ # comment.nest # => 'comments:123'
73
+ # Comment.new.nest # => 'comments:_'
74
+ #
75
+ def nest
76
+ self.class.namespace[id || '_']
77
+ end
78
+
79
+ end
@@ -0,0 +1,13 @@
1
+ class Redpear::Nest < ::Nest
2
+
3
+ [:mapped_hmget, :mapped_hmset].each do |meth|
4
+ define_method(meth) do |*args, &block|
5
+ redis.send(meth, self, *args, &block)
6
+ end
7
+ end
8
+
9
+ def mapped_hmget_all
10
+ mapped_hmget *hkeys
11
+ end
12
+
13
+ end
@@ -0,0 +1,134 @@
1
+ # Redpear's persistence methods
2
+ module Redpear::Persistence
3
+ extend Redpear::Concern
4
+
5
+ module ClassMethods
6
+
7
+ # Runs a bulk-operation.
8
+ # @yield [] operations that should be run in the transaction
9
+ def transaction(&block)
10
+ connection.multi(&block)
11
+ end
12
+
13
+ # Create or update a record. Example:
14
+ #
15
+ # Post.save :body => "Hello World!" # => creates a new Post
16
+ # Post.save :id => 3, :body => "Hello World!" # => updates an existing Post
17
+ #
18
+ def save(*args)
19
+ new(*args).tap(&:save)
20
+ end
21
+
22
+ # Destroys a record. Example:
23
+ # @param id the ID of the record to destroy
24
+ # @return [Redpear::Model] the destroyed record
25
+ def destroy(id)
26
+ new('id' => id).tap(&:destroy)
27
+ end
28
+
29
+ # Generates the next ID
30
+ def next_id
31
+ pk_nest.incr.to_s
32
+ end
33
+
34
+ end
35
+
36
+ # Returns true for new records
37
+ def new_record?
38
+ !id
39
+ end
40
+
41
+ # Returns true for existing records
42
+ def persisted?
43
+ !new_record?
44
+ end
45
+
46
+ # Reloads the record (destructive)
47
+ def reload
48
+ replace self.class.find(id, :lazy => false) if persisted?
49
+ self
50
+ end
51
+
52
+ # Load attributes from DB (destructive)
53
+ def refresh_attributes
54
+ update nest.mapped_hmget(*self.class.columns.names) if persisted?
55
+ @__loaded__ = true
56
+ self
57
+ end
58
+
59
+ # Saves the record.
60
+ #
61
+ # @param [Hash] options additional options
62
+ # @option options [Integer|Date] :expire expiration period or timestamp
63
+ # @yield [record] Additional block, applied as part of the save transaction
64
+ # @return [Redpear::Model] the saved record
65
+ def save(options = {}, &block)
66
+ before_save
67
+ update "id" => self.class.next_id unless persisted?
68
+
69
+ transaction do
70
+ nest.mapped_hmset __persistable_attributes__
71
+ __relevant_sets__.each {|s| s.sadd(id) }
72
+ expire options[:expire]
73
+ yield(self) if block
74
+ end
75
+ ensure
76
+ after_save
77
+ end
78
+
79
+ # Destroy the record.
80
+ # @return [Boolean] true or false
81
+ def destroy
82
+ return false unless persisted?
83
+
84
+ transaction do
85
+ nest.del
86
+ __relevant_sets__.each {|s| s.srem(id) }
87
+ end
88
+
89
+ true
90
+ end
91
+
92
+ protected
93
+
94
+ # Run in a DB transaction, returns self
95
+ def transaction(&block)
96
+ self.class.transaction(&block)
97
+ self
98
+ end
99
+
100
+ # "Cheap" callback, override in subclasses
101
+ def before_save
102
+ end
103
+
104
+ # "Cheap" callback, override in subclasses
105
+ def after_save
106
+ end
107
+
108
+ private
109
+
110
+ # Attributes that can be persisted
111
+ def __persistable_attributes__
112
+ result = {}
113
+ each do |key, value|
114
+ next if key == "id"
115
+ result[key] = __persistable_value__(value)
116
+ end
117
+ result
118
+ end
119
+
120
+ def __persistable_value__(value)
121
+ case value
122
+ when Time
123
+ value.to_i
124
+ else
125
+ value
126
+ end
127
+ end
128
+
129
+ # Return relevant set nests
130
+ def __relevant_sets__
131
+ @__relevant_sets__ ||= [self.class.mb_nest] + self.class.columns.indices.map {|i| i.nest self[i] }.compact
132
+ end
133
+
134
+ end
@@ -0,0 +1,34 @@
1
+ module Redpear::Schema
2
+ extend Redpear::Concern
3
+ autoload :Collection, 'redpear/schema/collection'
4
+
5
+ module ClassMethods
6
+
7
+ # @return [Redpear::Schema::Collection] the columns of this model
8
+ def columns
9
+ @columns ||= Redpear::Schema::Collection.new
10
+ end
11
+
12
+ # @param [multiple] the column definition. Please see Redpear::Column#initialize
13
+ def column(*args)
14
+ columns.column(self, *args).tap do |col|
15
+ __define_attribute_accessors__(col)
16
+ end
17
+ end
18
+
19
+ # @param [multiple] the index definition. Please see Redpear::Column#initialize
20
+ def index(*args)
21
+ columns.index(self, *args).tap do |col|
22
+ __define_attribute_accessors__(col)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def __define_attribute_accessors__(col)
29
+ define_method(col.name) { self[col.name] } if col.readable?
30
+ define_method("#{col.name}=") {|v| self[col.name] = v } if col.writable?
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,48 @@
1
+ # Stores the column information
2
+ class Redpear::Schema::Collection < Array
3
+
4
+ # @param [multiple] the column definition. Please see Redpear::Column#initialize
5
+ def column(*args)
6
+ reset!
7
+ Redpear::Column.new(*args).tap do |col|
8
+ self << col
9
+ end
10
+ end
11
+
12
+ # @param [multiple] the index definition. Please see Redpear::Column#initialize
13
+ def index(*args)
14
+ reset!
15
+ Redpear::Index.new(*args).tap do |col|
16
+ self << col
17
+ end
18
+ end
19
+
20
+ # @return [Array] the names of the columns
21
+ def names
22
+ @names ||= lookup.keys
23
+ end
24
+
25
+ # @return [Array] the names of the indices only
26
+ def indices
27
+ @indices ||= select(&:index?)
28
+ end
29
+
30
+ # @return [Hash] the column lookup, indexed by name
31
+ def lookup
32
+ @lookup ||= inject({}) {|r, c| r.update c.to_s => c }
33
+ end
34
+
35
+ # @param [String] the column name
36
+ # @return [Redpear::Column] the column for the given name
37
+ def [](name)
38
+ lookup[name.to_s]
39
+ end
40
+
41
+ # Resets indexes and lookups
42
+ def reset!
43
+ instance_variables.each do |name|
44
+ instance_variable_set name, nil
45
+ end
46
+ end
47
+
48
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redpear
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dimitrij Denissenko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-24 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: redis
16
+ requirement: &13539180 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *13539180
25
+ - !ruby/object:Gem::Dependency
26
+ name: nest
27
+ requirement: &13533640 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *13533640
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &13533060 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *13533060
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: &13532580 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *13532580
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: &13531860 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *13531860
69
+ - !ruby/object:Gem::Dependency
70
+ name: fakeredis
71
+ requirement: &13529820 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *13529820
80
+ - !ruby/object:Gem::Dependency
81
+ name: shoulda-matchers
82
+ requirement: &13528760 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *13528760
91
+ - !ruby/object:Gem::Dependency
92
+ name: machinist
93
+ requirement: &13527200 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ~>
97
+ - !ruby/object:Gem::Version
98
+ version: 2.0.0.beta
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *13527200
102
+ description: Simple, elegant & efficient ORM for Redis
103
+ email: dimitrij@blacksquaremedia.com
104
+ executables: []
105
+ extensions: []
106
+ extra_rdoc_files: []
107
+ files:
108
+ - lib/redpear.rb
109
+ - lib/redpear/connection.rb
110
+ - lib/redpear/schema/collection.rb
111
+ - lib/redpear/core_ext/stringify_keys.rb
112
+ - lib/redpear/concern.rb
113
+ - lib/redpear/namespace.rb
114
+ - lib/redpear/nest.rb
115
+ - lib/redpear/finders.rb
116
+ - lib/redpear/persistence.rb
117
+ - lib/redpear/expiration.rb
118
+ - lib/redpear/column.rb
119
+ - lib/redpear/model.rb
120
+ - lib/redpear/index.rb
121
+ - lib/redpear/schema.rb
122
+ - lib/redpear/machinist.rb
123
+ homepage: https://github.com/bsm/redpear
124
+ licenses: []
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: 1.8.7
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: 1.6.0
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 1.8.10
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Redpear, a Redis ORM
147
+ test_files: []