redpear 0.3.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.
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: []