hyperion 0.1.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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 adrianpike
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,68 @@
1
+ = hyperion
2
+
3
+ Hyperion is a lightning fast Ruby object model backed with Redis. It was created in 48 hours to back Leatherbound.me, a ebook comparison shopping tool that was built for Rails Rumble. We withstood 15,000 uniques on Monday morning, and Hyperion stayed super quick even with tons of concurrent queries on a small Linode.
4
+
5
+ == Key features
6
+
7
+ * CRUD on Ruby classes, any attributes get stuffed into a Redis key.
8
+ * Indexing of objects on any attributes or collections of attributes.
9
+
10
+ == A quickie
11
+
12
+ adrian$ gem install hyperion
13
+
14
+ require 'hyperion'
15
+
16
+ class Person < Hyperion
17
+ # First we set up the attributes on this object.
18
+ attr_accessor :name, :email, :password, :other_amazing_info, :car_color, :state
19
+
20
+ # Let's use the email as our primary key.
21
+ # Another option would be to not define our own, in which case :id will be created & used.
22
+ hyperion_key :email
23
+
24
+ # Lets add some indexes!
25
+ # We can search for all Persons with a given name.
26
+ hyperion_index :name
27
+
28
+ # We can also search for all people with a given car color and state.
29
+ hyperion_index [:car_color, :state]
30
+ end
31
+
32
+ p1 = Person.new({:name => 'Adrian Pike', :email => 'adrian@pikeapps.com', …})
33
+ p1.save
34
+
35
+ Person.find('adrian@pikeapps.com') #=> #<Person @name="Adrian Pike", @email="adrian@pikeapps.com">
36
+ Person.find(:name => 'Adrian Pike') #=> [#<Person @name="Adrian Pike", @email="adrian@pikeapps.com">]
37
+ Person.find(:car_color => 'purple', :state => 'denial') #=> [#<Person @name="Adrian Pike", @email="adrian@pikeapps.com">]
38
+
39
+ == Requirements
40
+
41
+ * Ruby 1.9
42
+ * activesupport
43
+ * Redis
44
+
45
+ == Roadmap
46
+
47
+ * Change the indexes to use ZSETs.
48
+ * Delete. (ha!)
49
+ * Schema versioning and migrations.
50
+ * Faster faster faster! Let's utilize more of Redis' exposed data structures to make it better.
51
+ * Clever auto-sharding based on primary keyspace
52
+ * Figure out a way to auto-balance shards
53
+ * Slow full-table searches.
54
+ * Partial index searches
55
+
56
+ == Note on Patches/Pull Requests
57
+
58
+ * Fork the project.
59
+ * Make your feature addition or bug fix.
60
+ * Add tests for it. This is important so I don't break it in a
61
+ future version unintentionally.
62
+ * Commit, do not mess with rakefile, version, or history.
63
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
64
+ * Send me a pull request. Bonus points for topic branches.
65
+
66
+ == Copyright
67
+
68
+ Copyright (c) 2010 Adrian Pike. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,84 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ gem 'rspec', '>= 1.2.6'
6
+ gem 'rspec-rails', '>= 1.2.6'
7
+ require 'spec'
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ begin
11
+ require 'rspec/core/rake_task.rb'
12
+ require 'rspec/core/version'
13
+ rescue LoadError
14
+ puts "[formtastic:] RSpec - or one of it's dependencies - is not available. Install it with: sudo gem install rspec-rails"
15
+ end
16
+ end
17
+
18
+ begin
19
+ require 'jeweler'
20
+ Jeweler::Tasks.new do |gem|
21
+ gem.name = "hyperion"
22
+ gem.summary = %Q{Hyperion is a sweet Ruby data model thats backed with Redis.}
23
+ gem.description = %Q{Hyperion is a sweet Ruby data model thats backed with Redis. It's designed to be screamin' fast.}
24
+ gem.email = "adrian.pike@gmail.com"
25
+ gem.homepage = "http://github.com/adrianpike/hyperion"
26
+ gem.authors = ["adrianpike"]
27
+ end
28
+ rescue LoadError
29
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
30
+ end
31
+
32
+ desc 'Default: run unit specs.'
33
+ task :default => :spec
34
+
35
+ require 'rake/rdoctask'
36
+ Rake::RDocTask.new do |rdoc|
37
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
38
+
39
+ rdoc.rdoc_dir = 'rdoc'
40
+ rdoc.title = "hyperion #{version}"
41
+ rdoc.rdoc_files.include('README*')
42
+ rdoc.rdoc_files.include('lib/**/*.rb')
43
+ end
44
+
45
+
46
+ if defined?(Spec)
47
+ desc 'Test hyperion.'
48
+ Spec::Rake::SpecTask.new('spec') do |t|
49
+ t.spec_files = FileList['spec/**/*_spec.rb']
50
+ t.spec_opts = ["-c"]
51
+ end
52
+
53
+ desc 'Test hyperion with specdoc formatting and colors'
54
+ Spec::Rake::SpecTask.new('specdoc') do |t|
55
+ t.spec_files = FileList['spec/**/*_spec.rb']
56
+ t.spec_opts = ["--format specdoc", "-c"]
57
+ end
58
+
59
+ desc "Run all examples with RCov"
60
+ Spec::Rake::SpecTask.new('examples_with_rcov') do |t|
61
+ t.spec_files = FileList['spec/**/*_spec.rb']
62
+ t.rcov = true
63
+ t.rcov_opts = ['--exclude', 'spec,Library']
64
+ end
65
+ end
66
+
67
+ if defined?(RSpec)
68
+ desc 'Test hyperion.'
69
+ RSpec::Core::RakeTask.new('spec') do |t|
70
+ t.pattern = FileList['spec/**/*_spec.rb']
71
+ end
72
+
73
+ desc 'Test hyperion with specdoc formatting and colors'
74
+ RSpec::Core::RakeTask.new('specdoc') do |t|
75
+ t.pattern = FileList['spec/**/*_spec.rb']
76
+ end
77
+
78
+ desc "Run all examples with RCov"
79
+ RSpec::Core::RakeTask.new('examples_with_rcov') do |t|
80
+ t.pattern = FileList['spec/**/*_spec.rb']
81
+ t.rcov = true
82
+ t.rcov_opts = ['--exclude', 'spec,Library']
83
+ end
84
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/benchmarks.rb ADDED
@@ -0,0 +1,100 @@
1
+ require 'benchmark'
2
+ require 'ruby-prof'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
5
+ require 'hyperion'
6
+
7
+ class DefaultKey < Hyperion
8
+ attr_accessor :content, :key
9
+ hyperion_key :key
10
+ end
11
+
12
+ class NoKey < Hyperion
13
+ attr_accessor :content
14
+ end
15
+
16
+ class IndexedObject < Hyperion
17
+ attr_accessor :content, :other_content, :key
18
+
19
+ hyperion_key :key
20
+ hyperion_index :other_content
21
+ hyperion_index [:content, :other_content]
22
+ end
23
+
24
+ [100,1000,10000,100000].each {|n|
25
+ puts "===== #{n} ====="
26
+ puts "#{n} inserts:"
27
+ bm = Benchmark.measure {
28
+ n.times do |k|
29
+ f = DefaultKey.new({
30
+ :key => k,
31
+ :content => "Benchmarking!"
32
+ })
33
+ f.save
34
+ end
35
+ }
36
+ p bm
37
+
38
+ puts "#{n} fetches:"
39
+ bm = Benchmark.measure {
40
+ n.times do |k|
41
+ f = DefaultKey.find(k)
42
+ end
43
+ }
44
+ p bm
45
+
46
+ puts "#{n} inserts w/autoincrement:"
47
+ bm = Benchmark.measure {
48
+ n.times do |k|
49
+ f = DefaultKey.new({
50
+ :content => "Benchmarking!"
51
+ })
52
+ f.save
53
+ end
54
+ }
55
+ p bm
56
+
57
+ puts "#{n} inserts with indexes and autoincrement:"
58
+ bm = Benchmark.measure {
59
+ n.times do |k|
60
+ f = IndexedObject.new({
61
+ :content => "Benchmarking!",
62
+ :other_content => k
63
+ })
64
+ f.save
65
+ end
66
+ }
67
+ p bm
68
+
69
+ puts "#{n} fetches on a single index:"
70
+ bm = Benchmark.measure {
71
+ n.times do |k|
72
+ f = IndexedObject.find({
73
+ :other_content => k
74
+ })
75
+ end
76
+ }
77
+ p bm
78
+
79
+ puts "#{n} fetches on a complex index:"
80
+ bm = Benchmark.measure {
81
+ n.times do |k|
82
+ f = IndexedObject.find({
83
+ :content => 'Benchmarking!',
84
+ :other_content => k
85
+ })
86
+ end
87
+ }
88
+ p bm
89
+
90
+
91
+ puts "#{n} indexless updates:"
92
+ bm = Benchmark.measure {
93
+ n.times do |k|
94
+ f = DefaultKey.find(k)
95
+ f.content = 'Second time around'
96
+ f.save
97
+ end
98
+ }
99
+ p bm
100
+ }
data/hyperion.gemspec ADDED
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{hyperion}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["adrianpike"]
12
+ s.date = %q{2010-10-28}
13
+ s.description = %q{Hyperion is a sweet Ruby data model thats backed with Redis. It's designed to be screamin' fast.}
14
+ s.email = %q{adrian.pike@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "benchmarks.rb",
27
+ "hyperion.gemspec",
28
+ "lib/hyperion.rb",
29
+ "lib/hyperion/base.rb",
30
+ "lib/hyperion/finders.rb",
31
+ "lib/hyperion/indices.rb",
32
+ "lib/hyperion/keys.rb",
33
+ "lib/hyperion/logger.rb",
34
+ "lib/hyperion/version.rb",
35
+ "spec/hyperion_spec.rb",
36
+ "spec/spec_helper.rb"
37
+ ]
38
+ s.homepage = %q{http://github.com/adrianpike/hyperion}
39
+ s.rdoc_options = ["--charset=UTF-8"]
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.3.7}
42
+ s.summary = %q{Hyperion is a sweet Ruby data model thats backed with Redis.}
43
+ s.test_files = [
44
+ "spec/hyperion_spec.rb",
45
+ "spec/spec_helper.rb"
46
+ ]
47
+
48
+ if s.respond_to? :specification_version then
49
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
50
+ s.specification_version = 3
51
+
52
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
53
+ else
54
+ end
55
+ else
56
+ end
57
+ end
58
+
data/lib/hyperion.rb ADDED
@@ -0,0 +1,168 @@
1
+ require 'active_support'
2
+ require 'yaml'
3
+ require 'redis'
4
+
5
+ # TODO: splat this
6
+ require 'hyperion/base'
7
+ require 'hyperion/indices'
8
+ require 'hyperion/keys'
9
+ require 'hyperion/logger'
10
+ require 'hyperion/version'
11
+
12
+ class Hyperion
13
+ extend Indices
14
+ extend Keys
15
+ extend Version
16
+ extend Logger
17
+
18
+ DEBUG = false
19
+ # TODO: ActiveModel lint it _mayhaps_
20
+ # TODO: atomic operations
21
+ # TODO: default key called #{class}_id if there isn't any @@redis_key
22
+
23
+ def initialize(opts = {})
24
+ defaults = (self.class.class_variable_defined?('@@redis_defaults') ? self.class.class_variable_get('@@redis_defaults') : {})
25
+
26
+ defaults.merge(opts).each {|k,v|
27
+ self.send(k.to_s+'=',v)
28
+ }
29
+ end
30
+
31
+ def self.first(conds)
32
+ # FIXME: gotta be a faster way ;)
33
+ self.find(conds).first
34
+ end
35
+
36
+ def self.find(conds)
37
+ Hyperion.logger.debug("[RS] Searching for #{conds.inspect}") if Hyperion::DEBUG
38
+
39
+ if conds.is_a? Hash then
40
+ Hyperion.logger.debug("[RS] Its a Hash, digging through indexes!") if Hyperion::DEBUG
41
+ ids = []
42
+ index_keys = []
43
+ index_values = []
44
+
45
+ if conds.keys.size > 1 then
46
+ conds.sort.each {|k,v|
47
+ index_values << v
48
+ index_keys << k.to_s
49
+ }
50
+ index_key = self.to_s.downcase + '_' + index_keys.join('.') + '_' + index_values.join('.')
51
+ ids << Hyperion.redis.smembers(index_key)
52
+ else
53
+ conds.each{|k,v|
54
+ index_key = self.to_s.downcase + '_' + k.to_s + '_' + v.to_s
55
+ ids << Hyperion.redis.smembers(index_key)
56
+ }
57
+ end
58
+ ids.flatten.uniq.collect{|i|
59
+ self.find(i)
60
+ }
61
+ else
62
+ Hyperion.logger.debug("[RS] Fetching #{self.to_s.downcase + '_' + conds.to_s}") if Hyperion::DEBUG
63
+ v = redis[self.to_s.downcase + '_' + conds.to_s].to_s
64
+ if v and not v.empty? then
65
+ self.deserialize(v)
66
+ else
67
+ nil
68
+ end
69
+ end
70
+ end
71
+
72
+ def self.deserialize(what); YAML.load(what); end
73
+ def serialize; YAML::dump(self); end
74
+
75
+ def save
76
+ Hyperion.logger.debug("[RS] Saving a #{self.class.to_s}:") if Hyperion::DEBUG
77
+
78
+ unless (self.class.class_variable_defined?('@@redis_key')) then
79
+ self.class.send('attr_accessor', 'id')
80
+ self.class.class_variable_set('@@redis_key', 'id')
81
+ end
82
+
83
+ unless (self.send(self.class.class_variable_get('@@redis_key'))) then
84
+ Hyperion.logger.debug("[RS] Generating new key!") if Hyperion::DEBUG
85
+ self.send(self.class.class_variable_get('@@redis_key').to_s + '=', new_key)
86
+ end
87
+
88
+ Hyperion.logger.debug("[RS] Saving into #{full_key}: #{self.inspect}") if Hyperion::DEBUG
89
+ Hyperion.redis[full_key] = self.serialize
90
+
91
+ # Now lets update any indexes
92
+ # BUG: need to clear out any old indexes of us
93
+ self.class.class_variable_get('@@redis_indexes').each{|idx|
94
+ Hyperion.logger.debug("[RS] Updating index for #{idx}") if Hyperion::DEBUG
95
+
96
+ if idx.is_a?(Array) then
97
+ index_values = idx.sort.collect {|i| self.send(i) }.join('.')
98
+ index_key = self.class.to_s.downcase + '_' + idx.sort.join('.').to_s + '_' + index_values
99
+ else
100
+ value = self.send(idx)
101
+ index_key = self.class.to_s.downcase + '_' + idx.to_s + '_' + value.to_s if value
102
+ end
103
+ Hyperion.logger.debug("[RS] Saving index #{index_key}: #{self.send(self.class.class_variable_get('@@redis_key'))}") if Hyperion::DEBUG
104
+ Hyperion.redis.sadd(index_key, self.send(self.class.class_variable_get('@@redis_key')))
105
+ } if self.class.class_variable_defined?('@@redis_indexes')
106
+ end
107
+
108
+ def self.dump(output = STDOUT, lock = false)
109
+ # TODO: lockability and progress
110
+ output.write(<<-eos)
111
+ # Hyperion Dump
112
+ # Generated by @adrianpike's Hyperion gem.
113
+ eos
114
+ output.write('# Generated on ' + Time.current.to_s + "\n")
115
+ output.write('# DB size is ' + redis.dbsize.to_s + "\n")
116
+
117
+ redis.keys.each{|k|
118
+ case redis.type(k)
119
+ when "string"
120
+ output.write({ k => redis.get(k)}.to_yaml)
121
+ when "set"
122
+ output.write({ k => redis.smembers(k) }.to_yaml)
123
+ end
124
+ }
125
+ end
126
+
127
+
128
+ # THIS IS MAD DANGEROUS AND UNTESTED, BEWARE DATA INTEGRITY
129
+ def self.load(file = STDIN, truncate = true, lock = false)
130
+ # TODO: lockability and progress
131
+
132
+ YAML.each_document( file ) do |ydoc|
133
+ ydoc.each {|k,v|
134
+ redis.del(k) if truncate
135
+
136
+ case v.class.to_s
137
+ when 'String'
138
+ redis[k] = v
139
+ when 'Array'
140
+ v.each{|val|
141
+ redis.sadd(k,val)
142
+ }
143
+ else
144
+ p v.class
145
+ end
146
+ }
147
+ end
148
+
149
+ end
150
+
151
+ # THIS IS TOTALLY IRREVERSIBLE YO
152
+ def self.truncate!
153
+ redis.flushdb
154
+ end
155
+
156
+ private
157
+ def new_key
158
+ if (self.class.class_variable_defined?('@@redis_generate_key') and self.class.class_variable_get('@@redis_generate_key') == false)
159
+ raise NoKey
160
+ else
161
+ Hyperion.redis.incr(self.class.to_s.downcase + '_' + self.class.class_variable_get('@@redis_key').to_s)
162
+ end
163
+ end
164
+
165
+ def full_key
166
+ self.class.to_s.downcase + '_' + self.send(self.class.class_variable_get('@@redis_key')).to_s
167
+ end
168
+ end
@@ -0,0 +1,14 @@
1
+ class Hyperion
2
+
3
+ class HyperionException < Exception; end
4
+ class NoKey < HyperionException; end
5
+
6
+ def self.hyperion_defaults(defaults)
7
+ class_variable_set(:@@redis_defaults, defaults)
8
+ end
9
+
10
+ def self.redis
11
+ @@redis ||= Redis.new
12
+ end
13
+
14
+ end
@@ -0,0 +1,12 @@
1
+ class Hyperion
2
+ module Finders
3
+
4
+ # find(id)
5
+ # find(:all, {})
6
+ # find(:first, {})
7
+ # find(:last, {})
8
+
9
+ # we can do > and < if we use ZRANGEBYSCORE, but scores have to be integer :/
10
+
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ class Hyperion
2
+ module Indices
3
+
4
+ class NoIndex < HyperionException; end
5
+
6
+ def hyperion_index(index)
7
+ if class_variable_defined?(:@@redis_indexes) then
8
+ class_variable_set(:@@redis_indexes, class_variable_get(:@@redis_indexes) << index)
9
+ else
10
+ class_variable_set(:@@redis_indexes, [index])
11
+ end
12
+ end
13
+
14
+ def reindex!
15
+ # TODO
16
+ end
17
+
18
+ # Indexes need to be sorted sets!
19
+ # ZSET scores can supposedly be large floats, should explore max ZSET score size.
20
+
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ class Hyperion
2
+ module Keys
3
+
4
+ def hyperion_key(key, opts = {})
5
+ class_variable_set(:@@redis_key, key)
6
+ class_variable_set(:@@redis_generate_key, opts[:generate])
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ require 'logger'
2
+
3
+ class Hyperion
4
+ module Logger
5
+
6
+ def logger
7
+ unless class_variable_defined?('@@logger') then
8
+ @@logger = ::Logger.new(STDOUT)
9
+ @@logger.level = ::Logger::DEBUG
10
+ end
11
+
12
+ @@logger
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ class Hyperion
2
+ module Version
3
+
4
+ def branch; nil; end
5
+ def major; 0; end
6
+ def minor; 1; end
7
+ def patch; 0; end
8
+
9
+ def version
10
+ [branch,major,minor,patch].compact.collect(&:to_s).join('.')
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+ # TODO: truncate the redis store and/or stub it beforehand
3
+
4
+ describe 'Hyperion' do
5
+ it "should work for basic set/fetch" do
6
+ string1 = random_string
7
+
8
+ f = DefaultKey.new
9
+ f.key = 'test'
10
+ f.content = string1
11
+ f.save
12
+
13
+ f2 = DefaultKey.find('test')
14
+ f2.content.should == f.content
15
+
16
+ f3 = IndexedObject.find(string1)
17
+ f3.should == nil
18
+ end
19
+
20
+ it "should be able to index a single attribute" do
21
+ string1 = random_string
22
+
23
+ f = IndexedObject.new
24
+ f.content = string1
25
+ f.other_content = 'zero_cool'
26
+ f.key = 'testery'
27
+ f.save
28
+
29
+ f2 = IndexedObject.first(:other_content => 'zero_cool')
30
+ f2.content.should == f.content
31
+
32
+ f3 = IndexedObject.find('testery')
33
+ f3.content.should == f.content
34
+ end
35
+
36
+ it "should take objects at instantiation time" do
37
+ string1 = random_string
38
+
39
+ n=NoKey.new(:content => string1)
40
+ n.save
41
+
42
+ n2 = NoKey.find(n.id)
43
+ n2.content.should == string1
44
+ end
45
+
46
+ it "should autogenerate keys" do
47
+ string1 = random_string
48
+
49
+ n=NoKey.new
50
+ n.content = string1
51
+ n.save
52
+
53
+ n2 = NoKey.find(n.id)
54
+ n2.content.should == string1
55
+ end
56
+
57
+ it 'should autoincrement keys whether specified or not' do
58
+ end
59
+
60
+ it "should be able to index multiple attributes" do
61
+ string1 = random_string
62
+
63
+ f = IndexedObject.new
64
+ f.content = string1
65
+ f.other_content = 'zero_cool'
66
+ f.key = 'testery'
67
+ f.save
68
+
69
+ f2 = IndexedObject.first(:other_content => 'zero_cool')
70
+ f2.content.should == f.content
71
+
72
+ f3 = IndexedObject.find('testery')
73
+ f3.content.should == f.content
74
+
75
+ f4 = IndexedObject.first(:other_content => 'zero_cool', :content => string1)
76
+ f4.content.should == f.content
77
+ end
78
+
79
+ it "shouldn't put an index in for an empty value" do
80
+ string1 = random_string
81
+
82
+ f = IndexedObject.new
83
+ f.content = string1
84
+ f.save
85
+
86
+ f2 = IndexedObject.first(:other_content => nil)
87
+ f2.should == nil
88
+ end
89
+
90
+ it "shouldn't matter what order your indexes are specified" do
91
+ end
92
+
93
+ it "should not have concurrency issues" do
94
+ end
95
+
96
+ it "should be able to reindex objects" do
97
+ end
98
+
99
+ it "should update indexes" do
100
+ end
101
+
102
+ it 'should be OK with key collision' do
103
+ end
104
+
105
+ it "should die if there's no redis server around" do
106
+ end
107
+
108
+ it "should allow crazy lengths and contents for both keys and values" do
109
+ end
110
+
111
+ end
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ require 'hyperion'
6
+
7
+ RSpec.configure do |config|
8
+ config.mock_with :rspec
9
+ end
10
+
11
+ class DefaultKey < Hyperion
12
+ attr_accessor :content, :key
13
+ hyperion_key :key
14
+ end
15
+
16
+ class NoKey < Hyperion
17
+ attr_accessor :content
18
+ end
19
+
20
+ class IndexedObject < Hyperion
21
+ attr_accessor :content, :other_content, :key
22
+
23
+ hyperion_key :key
24
+ hyperion_index :other_content
25
+ hyperion_index [:content, :other_content]
26
+ end
27
+
28
+ def random_string(length = 10)
29
+ 'asdfdsfasd' # todo lolololol
30
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyperion
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - adrianpike
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-10-28 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Hyperion is a sweet Ruby data model thats backed with Redis. It's designed to be screamin' fast.
22
+ email: adrian.pike@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - LICENSE
29
+ - README.rdoc
30
+ files:
31
+ - .document
32
+ - .gitignore
33
+ - LICENSE
34
+ - README.rdoc
35
+ - Rakefile
36
+ - VERSION
37
+ - benchmarks.rb
38
+ - hyperion.gemspec
39
+ - lib/hyperion.rb
40
+ - lib/hyperion/base.rb
41
+ - lib/hyperion/finders.rb
42
+ - lib/hyperion/indices.rb
43
+ - lib/hyperion/keys.rb
44
+ - lib/hyperion/logger.rb
45
+ - lib/hyperion/version.rb
46
+ - spec/hyperion_spec.rb
47
+ - spec/spec_helper.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/adrianpike/hyperion
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --charset=UTF-8
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.7
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Hyperion is a sweet Ruby data model thats backed with Redis.
80
+ test_files:
81
+ - spec/hyperion_spec.rb
82
+ - spec/spec_helper.rb