BrianTheCoder-cool_breeze 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/History.txt +4 -0
- data/README.txt +56 -0
- data/Rakefile +56 -0
- data/VERSION.yml +4 -0
- data/bin/cool_breeze +19 -0
- data/bin/redis.conf +66 -0
- data/lib/cool_breeze.rb +52 -0
- data/lib/cool_breeze/association.rb +52 -0
- data/lib/cool_breeze/conditions.rb +81 -0
- data/lib/cool_breeze/connections.rb +10 -0
- data/lib/cool_breeze/index/abstract.rb +19 -0
- data/lib/cool_breeze/index/class_index.rb +6 -0
- data/lib/cool_breeze/index/instance_index.rb +9 -0
- data/lib/cool_breeze/mixins/associations.rb +25 -0
- data/lib/cool_breeze/mixins/indices.rb +55 -0
- data/lib/cool_breeze/model.rb +140 -0
- data/lib/cool_breeze/query.rb +36 -0
- data/lib/cool_breeze/types/abstract.rb +19 -0
- data/lib/cool_breeze/types/counter.rb +13 -0
- data/lib/cool_breeze/types/list.rb +41 -0
- data/lib/cool_breeze/types/set.rb +29 -0
- data/lib/cool_breeze/types/value.rb +9 -0
- data/spec/association_spec.rb +35 -0
- data/spec/index_spec.rb +11 -0
- data/spec/model_spec.rb +23 -0
- data/spec/models/message.rb +12 -0
- data/spec/models/user.rb +5 -0
- data/spec/query_spec.rb +6 -0
- data/spec/spec_helper.rb +25 -0
- metadata +88 -0
data/History.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
cool_breeze
|
2
|
+
by Brian Smith
|
3
|
+
http://brianthecoder.com
|
4
|
+
brian@46blocks.com
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
The most awesomest thing ever
|
9
|
+
|
10
|
+
== FEATURES/PROBLEMS:
|
11
|
+
|
12
|
+
* need to write tests for queries
|
13
|
+
* uniqueness validator
|
14
|
+
* finish up indexes
|
15
|
+
* allow other data stores (bit table, simple db, couch?)
|
16
|
+
|
17
|
+
== SYNOPSIS:
|
18
|
+
|
19
|
+
* coming soon
|
20
|
+
|
21
|
+
== REQUIREMENTS:
|
22
|
+
|
23
|
+
* guid
|
24
|
+
* extlib
|
25
|
+
* validatable
|
26
|
+
* redis
|
27
|
+
* rufus-tokyo (ruby-tokyotyrant when its ready)
|
28
|
+
|
29
|
+
== INSTALL:
|
30
|
+
|
31
|
+
* its a gem on github, you should know the routine by now
|
32
|
+
|
33
|
+
== LICENSE:
|
34
|
+
|
35
|
+
(The MIT License)
|
36
|
+
|
37
|
+
Copyright (c) 2008 FIXME (different license?)
|
38
|
+
|
39
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
40
|
+
a copy of this software and associated documentation files (the
|
41
|
+
'Software'), to deal in the Software without restriction, including
|
42
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
43
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
44
|
+
permit persons to whom the Software is furnished to do so, subject to
|
45
|
+
the following conditions:
|
46
|
+
|
47
|
+
The above copyright notice and this permission notice shall be
|
48
|
+
included in all copies or substantial portions of the Software.
|
49
|
+
|
50
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
51
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
52
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
53
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
54
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
55
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
56
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "cool_breeze"
|
8
|
+
gem.summary = %Q{A new type of orm utilizing two different storage mechanisms to the best of their abilities}
|
9
|
+
gem.email = "wbsmith83@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/BrianTheCoder/cool_breeze"
|
11
|
+
gem.authors = ["brianthecoder"]
|
12
|
+
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = false
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
if File.exist?('VERSION.yml')
|
45
|
+
config = YAML.load(File.read('VERSION.yml'))
|
46
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "Cool Breeze #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
56
|
+
|
data/VERSION.yml
ADDED
data/bin/cool_breeze
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
puts "Its getting cold in here"
|
3
|
+
|
4
|
+
system("redis-server redis.conf")
|
5
|
+
|
6
|
+
puts "Redis started"
|
7
|
+
ROOT = File.expand_path(File.join(File.dirname(__FILE__)),'..')
|
8
|
+
|
9
|
+
TokyoConfig = {
|
10
|
+
:port => 45000,
|
11
|
+
:data_file => File.join(ROOT,'log','tokyo.tct'),
|
12
|
+
:pid_file => File.join(ROOT,'log','tokyo.pid'),
|
13
|
+
:log_file => File.join(ROOT,'log','tokyo.log')
|
14
|
+
}
|
15
|
+
|
16
|
+
system("ttserver -dmn -port #{TokyoConfig[:port]} -log #{TokyoConfig[:log_file]} -pid #{TokyoConfig[:pid_file]} #{TokyoConfig[:data_file]}")
|
17
|
+
|
18
|
+
puts "Tokyo server started"
|
19
|
+
puts "Lets rock and roll!"
|
data/bin/redis.conf
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Redis configuration file example
|
2
|
+
|
3
|
+
# By default Redis does not run as a daemon. Use 'yes' if you need it.
|
4
|
+
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
|
5
|
+
daemonize yes
|
6
|
+
|
7
|
+
# Accept connections on the specified port, default is 6379
|
8
|
+
port 6379
|
9
|
+
|
10
|
+
# If you want you can bind a single interface, if the bind option is not
|
11
|
+
# specified all the interfaces will listen for connections.
|
12
|
+
#
|
13
|
+
# bind 127.0.0.1
|
14
|
+
|
15
|
+
# Close the connection after a client is idle for N seconds
|
16
|
+
timeout 300
|
17
|
+
|
18
|
+
# Save the DB on disk:
|
19
|
+
#
|
20
|
+
# save <seconds> <changes>
|
21
|
+
#
|
22
|
+
# Will save the DB if both the given number of seconds and the given
|
23
|
+
# number of write operations against the DB occurred.
|
24
|
+
#
|
25
|
+
# In the example below the behaviour will be to save:
|
26
|
+
# after 900 sec (15 min) if at least 1 key changed
|
27
|
+
# after 300 sec (5 min) if at least 10 keys changed
|
28
|
+
# after 60 sec if at least 10000 keys changed
|
29
|
+
save 900 1
|
30
|
+
save 300 10
|
31
|
+
save 60 10000
|
32
|
+
|
33
|
+
# For default save/load DB in/from the working directory
|
34
|
+
# Note that you must specify a directory not a file name.
|
35
|
+
dir ./
|
36
|
+
|
37
|
+
# Set server verbosity to 'debug'
|
38
|
+
# it can be one of:
|
39
|
+
# debug (a lot of information, useful for development/testing)
|
40
|
+
# notice (moderately verbose, what you want in production probably)
|
41
|
+
# warning (only very important / critical messages are logged)
|
42
|
+
loglevel debug
|
43
|
+
|
44
|
+
# Specify the log file name. Also 'stdout' can be used to force
|
45
|
+
# the demon to log on the standard output. Note that if you use standard
|
46
|
+
# output for logging but daemonize, logs will be sent to /dev/null
|
47
|
+
logfile stdout
|
48
|
+
|
49
|
+
# Set the number of databases.
|
50
|
+
databases 16
|
51
|
+
|
52
|
+
################################# REPLICATION #################################
|
53
|
+
|
54
|
+
# Master-Slave replication. Use slaveof to make a Redis instance a copy of
|
55
|
+
# another Redis server. Note that the configuration is local to the slave
|
56
|
+
# so for example it is possible to configure the slave to save the DB with a
|
57
|
+
# different interval, or to listen to another port, and so on.
|
58
|
+
|
59
|
+
# slaveof <masterip> <masterport>
|
60
|
+
|
61
|
+
############################### ADVANCED CONFIG ###############################
|
62
|
+
|
63
|
+
# Glue small output buffers together in order to send small replies in a
|
64
|
+
# single TCP packet. Uses a bit more CPU but most of the times it is a win
|
65
|
+
# in terms of number of queries per second. Use 'yes' if unsure.
|
66
|
+
glueoutputbuf yes
|
data/lib/cool_breeze.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'extlib'
|
2
|
+
require 'guid'
|
3
|
+
require 'validatable'
|
4
|
+
|
5
|
+
module CoolBreeze
|
6
|
+
|
7
|
+
# :stopdoc:
|
8
|
+
VERSION = '0.2.0'
|
9
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
10
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
11
|
+
# :startdoc:
|
12
|
+
|
13
|
+
# Returns the version string for the library.
|
14
|
+
#
|
15
|
+
def self.version
|
16
|
+
VERSION
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the library path for the module. If any arguments are given,
|
20
|
+
# they will be joined to the end of the libray path using
|
21
|
+
# <tt>File.join</tt>.
|
22
|
+
#
|
23
|
+
def self.libpath( *args )
|
24
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the lpath for the module. If any arguments are given,
|
28
|
+
# they will be joined to the end of the path using
|
29
|
+
# <tt>File.join</tt>.
|
30
|
+
#
|
31
|
+
def self.path( *args )
|
32
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Utility method used to require all files ending in .rb that lie in the
|
36
|
+
# directory below this file that has the same name as the filename passed
|
37
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
38
|
+
# the _filename_ does not have to be equivalent to the directory.
|
39
|
+
#
|
40
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
41
|
+
dir ||= ::File.basename(fname, '.*')
|
42
|
+
search_me = ::File.expand_path(
|
43
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
44
|
+
|
45
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
46
|
+
end
|
47
|
+
|
48
|
+
end # module CoolBreeze
|
49
|
+
|
50
|
+
CoolBreeze.require_all_libs_relative_to(__FILE__)
|
51
|
+
|
52
|
+
# EOF
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
class Association
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(klass, name, options = {})
|
6
|
+
@list_name = "#{name}_list"
|
7
|
+
@klass = klass
|
8
|
+
klass.instance_index :"#{name}_list", :list
|
9
|
+
end
|
10
|
+
|
11
|
+
def for(instance)
|
12
|
+
@list = instance.method(@list_name)
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def add(obj)
|
17
|
+
@list.call.push(obj.key)
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove(obj)
|
21
|
+
@list.call.remove(1,obj.key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def destroy(obj)
|
25
|
+
remove(obj)
|
26
|
+
obj.destroy
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(start = 0, stop = -1)
|
30
|
+
keys = @list.call.range(start, stop)
|
31
|
+
return [] if keys.blank?
|
32
|
+
keys.map{|k| @klass.get k}
|
33
|
+
end
|
34
|
+
|
35
|
+
def size
|
36
|
+
@list.call.length
|
37
|
+
end
|
38
|
+
|
39
|
+
def first
|
40
|
+
key = @list.call.range(0,1)
|
41
|
+
@klass.get(key) unless key.blank?
|
42
|
+
end
|
43
|
+
|
44
|
+
def each
|
45
|
+
values = @list.call.range(0,-1)
|
46
|
+
return if values.blank?
|
47
|
+
values.each do |val|
|
48
|
+
yield @klass.get val
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class Symbol
|
2
|
+
def eq
|
3
|
+
[self.to_s,:eq]
|
4
|
+
end
|
5
|
+
|
6
|
+
def includes
|
7
|
+
[self.to_s,:includes]
|
8
|
+
end
|
9
|
+
|
10
|
+
def starts_with
|
11
|
+
[self.to_s,:starts_with]
|
12
|
+
end
|
13
|
+
|
14
|
+
def ends_with
|
15
|
+
[self.to_s,:ends_with]
|
16
|
+
end
|
17
|
+
|
18
|
+
def and
|
19
|
+
[self.to_s,:and]
|
20
|
+
end
|
21
|
+
|
22
|
+
def or
|
23
|
+
[self.to_s,:or]
|
24
|
+
end
|
25
|
+
|
26
|
+
def stroreq
|
27
|
+
[self.to_s,:stroreq]
|
28
|
+
end
|
29
|
+
|
30
|
+
def matches
|
31
|
+
[self.to_s,:matches]
|
32
|
+
end
|
33
|
+
|
34
|
+
def gt
|
35
|
+
[self.to_s,:gt]
|
36
|
+
end
|
37
|
+
|
38
|
+
def gte
|
39
|
+
[self.to_s,:gte]
|
40
|
+
end
|
41
|
+
|
42
|
+
def lt
|
43
|
+
[self.to_s,:lt]
|
44
|
+
end
|
45
|
+
|
46
|
+
def lte
|
47
|
+
[self.to_s,:lte]
|
48
|
+
end
|
49
|
+
|
50
|
+
def between
|
51
|
+
[self.to_s,:between]
|
52
|
+
end
|
53
|
+
|
54
|
+
def numoreq
|
55
|
+
[self.to_s,:numoreq]
|
56
|
+
end
|
57
|
+
|
58
|
+
def strasc
|
59
|
+
[self.to_s,:strasc]
|
60
|
+
end
|
61
|
+
|
62
|
+
def strdesc
|
63
|
+
[self.to_s,:strdesc]
|
64
|
+
end
|
65
|
+
|
66
|
+
def asc
|
67
|
+
[self.to_s,:asc]
|
68
|
+
end
|
69
|
+
|
70
|
+
def desc
|
71
|
+
[self.to_s,:desc]
|
72
|
+
end
|
73
|
+
|
74
|
+
def numasc
|
75
|
+
[self.to_s,:numasc]
|
76
|
+
end
|
77
|
+
|
78
|
+
def numdesc
|
79
|
+
[self.to_s,:numdesc]
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
module Index
|
3
|
+
class Abstract
|
4
|
+
attr_accessor :name, :type
|
5
|
+
def initialize(name,type)
|
6
|
+
@name = name
|
7
|
+
@type = type
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_key(klass)
|
11
|
+
@get_key ||= "#{klass.to_s.downcase}:#{@name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def type_klass
|
15
|
+
@type_klass ||= Module.find_const("CoolBreeze::Indices::#{@type.to_s.to_const_string}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
module Mixins
|
3
|
+
module Associations
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval <<-EOS, __FILE__, __LINE__
|
6
|
+
class_inheritable_accessor(:associations)
|
7
|
+
self.associations = {}
|
8
|
+
EOS
|
9
|
+
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def many(name, options = {})
|
16
|
+
associations[name] = CoolBreeze::Association.new(self,name,options = {})
|
17
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
18
|
+
def #{name}
|
19
|
+
self.class.associations[:"#{name}"].for(self)
|
20
|
+
end
|
21
|
+
EOS
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
module Mixins
|
3
|
+
module Indices
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval <<-EOS, __FILE__, __LINE__
|
6
|
+
class_inheritable_accessor(:index_types)
|
7
|
+
class_inheritable_accessor(:class_indices)
|
8
|
+
class_inheritable_accessor(:instance_indices)
|
9
|
+
self.index_types = %w(value list set counter)
|
10
|
+
self.class_indices = {}
|
11
|
+
self.instance_indices = {}
|
12
|
+
EOS
|
13
|
+
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
|
16
|
+
def destroy
|
17
|
+
instance_indices.each{|idx| idx.destroy }
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def class_indicies
|
24
|
+
@class_indicies
|
25
|
+
end
|
26
|
+
|
27
|
+
def instance_indicies
|
28
|
+
@instance_indicies
|
29
|
+
end
|
30
|
+
|
31
|
+
def class_index(name,type)
|
32
|
+
klass = Module.find_const("CoolBreeze::Types::#{type.to_s.to_const_string}")
|
33
|
+
instance_eval <<-RUBY, __FILE__, __LINE__
|
34
|
+
class_indices[name] = klass.new("#{self.to_s.downcase}:#{name}")
|
35
|
+
def #{name}(opts = {})
|
36
|
+
class_indices[:"#{name}"]
|
37
|
+
end
|
38
|
+
RUBY
|
39
|
+
end
|
40
|
+
|
41
|
+
def instance_index(name,type)
|
42
|
+
klass = Module.find_const("CoolBreeze::Types::#{type.to_s.to_const_string}")
|
43
|
+
class_eval do
|
44
|
+
define_method name do
|
45
|
+
if instance_indices[name].nil?
|
46
|
+
instance_indices[name] = klass.new("#{self.key}:#{name}")
|
47
|
+
end
|
48
|
+
instance_indices[name]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
module Model
|
3
|
+
def self.included(model)
|
4
|
+
model.send(:include, InstanceMethods)
|
5
|
+
model.send(:include, Validatable)
|
6
|
+
model.send(:include, Extlib::Hook)
|
7
|
+
model.send(:include, CoolBreeze::Mixins::Indices)
|
8
|
+
model.send(:include, CoolBreeze::Mixins::Associations)
|
9
|
+
model.class_inheritable_accessor(:default_conditions)
|
10
|
+
model.extend(ClassMethods)
|
11
|
+
model.class_eval do
|
12
|
+
self.default_conditions = {:model_type => self.to_s.downcase}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
module ClassMethods
|
16
|
+
def get(id)
|
17
|
+
attrs = data_store[id]
|
18
|
+
cast(attrs) unless attrs.blank?
|
19
|
+
end
|
20
|
+
|
21
|
+
alias :[] :get
|
22
|
+
|
23
|
+
def find(query = {})
|
24
|
+
rs = CoolBreeze::Query.new(self,query).run
|
25
|
+
l = LazyArray.new
|
26
|
+
l = rs.to_a
|
27
|
+
end
|
28
|
+
|
29
|
+
def first(query = {})
|
30
|
+
attrs = find(query.merge(default_conditions.merge(:limit => 1))).first
|
31
|
+
cast(attrs) unless attrs.blank?
|
32
|
+
end
|
33
|
+
|
34
|
+
def index_store
|
35
|
+
adapter(:redis)
|
36
|
+
end
|
37
|
+
|
38
|
+
def data_store
|
39
|
+
adapter(:tokyo)
|
40
|
+
end
|
41
|
+
|
42
|
+
def all(query = {})
|
43
|
+
attrs = find(query.merge(default_conditions))
|
44
|
+
attrs.map{|a| cast(a)} unless attrs.blank?
|
45
|
+
end
|
46
|
+
|
47
|
+
def adapter(name)
|
48
|
+
Connections.adapters[name]
|
49
|
+
end
|
50
|
+
|
51
|
+
def create(data = {})
|
52
|
+
m = self.new(data)
|
53
|
+
m.save
|
54
|
+
end
|
55
|
+
|
56
|
+
def timestamps!
|
57
|
+
before :save do
|
58
|
+
self.created_at = Time.now if new?
|
59
|
+
self.updated_at = Time.now
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def count(query = {})
|
64
|
+
find(query.merge(default_conditions)).size
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def cast(attrs)
|
70
|
+
key = attrs.delete(:pk)
|
71
|
+
m = self.new(attrs)
|
72
|
+
m.key = key
|
73
|
+
m
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module InstanceMethods
|
78
|
+
def initialize(data = {})
|
79
|
+
@index_store = self.class.index_store
|
80
|
+
@data_store = self.class.data_store
|
81
|
+
@data = {}
|
82
|
+
data.each do |k,v|
|
83
|
+
self.send(:"#{k}=",v.to_s)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def properties
|
88
|
+
@data.keys
|
89
|
+
end
|
90
|
+
|
91
|
+
def key
|
92
|
+
@key ||= "#{self.class.to_s.downcase}:#{Guid.new.to_s}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def key=(k)
|
96
|
+
@key = k
|
97
|
+
end
|
98
|
+
|
99
|
+
def new?
|
100
|
+
@data_store[key].nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
alias :new_record? :new?
|
104
|
+
|
105
|
+
def [](key)
|
106
|
+
@data[key]
|
107
|
+
end
|
108
|
+
|
109
|
+
def []=(key,val)
|
110
|
+
@data[key] = val.to_s
|
111
|
+
end
|
112
|
+
|
113
|
+
def save
|
114
|
+
unless @data.has_key?(:model_type)
|
115
|
+
self.model_type = self.class.to_s.downcase
|
116
|
+
end
|
117
|
+
@data_store[key] = @data
|
118
|
+
return @data_store[key] == @data
|
119
|
+
end
|
120
|
+
|
121
|
+
def destroy
|
122
|
+
@data_store.delete(key)
|
123
|
+
end
|
124
|
+
|
125
|
+
def method_missing(method_symbol, *arguments)
|
126
|
+
method_name = method_symbol.to_s
|
127
|
+
|
128
|
+
case method_name[-1..-1]
|
129
|
+
when "="
|
130
|
+
@data[method_name[0..-2]] = arguments.first.to_s
|
131
|
+
when "?"
|
132
|
+
@data[method_name[0..-2]] == true
|
133
|
+
else
|
134
|
+
# Returns nil on failure so forms will work
|
135
|
+
@data.has_key?(method_name) ? @data[method_name] : nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
class Query
|
3
|
+
def initialize(klass,conditions = {})
|
4
|
+
@klass = klass
|
5
|
+
@query = Rufus::Tokyo::TableQuery.new(CoolBreeze::Connections.adapters[:tokyo])
|
6
|
+
parse_conditions(conditions)
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse_conditions(conds)
|
10
|
+
# check for order condtion
|
11
|
+
order = conds.delete(:order_by)
|
12
|
+
@query.order_by(*order) unless order.nil?
|
13
|
+
# check for limit condition
|
14
|
+
offset = conds.delete(:offset)
|
15
|
+
limit = conds.delete(:limit)
|
16
|
+
@query.limit(limit, offset || -1) unless limit.nil?
|
17
|
+
# take conditions from :prop.op => val to add(prop, op, value)
|
18
|
+
conds.each do |key, val|
|
19
|
+
key = [key,:eq] if key.is_a?(Symbol)
|
20
|
+
if key.last == :eq
|
21
|
+
if val.is_a? Numeric
|
22
|
+
key[1] = :numeq
|
23
|
+
else val.is_a? String
|
24
|
+
key[1] = :streq
|
25
|
+
end
|
26
|
+
end
|
27
|
+
key[0] = key.first.to_s
|
28
|
+
@query.add *(key + [val.to_s])
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
@query.run
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
module Types
|
3
|
+
class Abstract
|
4
|
+
attr_accessor :key, :index_store
|
5
|
+
def initialize(key)
|
6
|
+
@key = key
|
7
|
+
@index_store = CoolBreeze::Connections.adapters[:redis]
|
8
|
+
end
|
9
|
+
|
10
|
+
def get
|
11
|
+
@index_store[@key]
|
12
|
+
end
|
13
|
+
|
14
|
+
def destroy
|
15
|
+
@index_store.delete(@key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
module Types
|
3
|
+
class List < Abstract
|
4
|
+
def length
|
5
|
+
@index_store.list_length(@key)
|
6
|
+
end
|
7
|
+
|
8
|
+
def push(val)
|
9
|
+
@index_store.push_tail(@key, val)
|
10
|
+
end
|
11
|
+
|
12
|
+
def pop
|
13
|
+
@index_store.pop_tail(@key)
|
14
|
+
end
|
15
|
+
|
16
|
+
def unshift
|
17
|
+
@index_store.pop_head(@key)
|
18
|
+
end
|
19
|
+
|
20
|
+
def shift(val)
|
21
|
+
@index_store.push_head(@key, val)
|
22
|
+
end
|
23
|
+
|
24
|
+
def trim(start, stop)
|
25
|
+
@index_store.list_trim(@key, start, stop)
|
26
|
+
end
|
27
|
+
|
28
|
+
def range(start, stop)
|
29
|
+
@index_store.list_range(@key, start, stop)
|
30
|
+
end
|
31
|
+
|
32
|
+
def index(index)
|
33
|
+
@index_store.list_index(@key, index)
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove(count, value)
|
37
|
+
@index_store.list_rm(@key, count, value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CoolBreeze
|
2
|
+
module Types
|
3
|
+
class Set < Abstract
|
4
|
+
def get
|
5
|
+
@index_store.set_members(@key)
|
6
|
+
end
|
7
|
+
|
8
|
+
def add(val)
|
9
|
+
@index_store.set_add(@key,val)
|
10
|
+
end
|
11
|
+
|
12
|
+
def size
|
13
|
+
@index_store.set_count(@key)
|
14
|
+
end
|
15
|
+
|
16
|
+
def include?(val)
|
17
|
+
@index_store.set_member?(@key, val)
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove(val)
|
21
|
+
@index_store.set_delete(@key, val)
|
22
|
+
end
|
23
|
+
|
24
|
+
def intersect(key)
|
25
|
+
@index_store.set_intersect(@key, key)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
2
|
+
|
3
|
+
describe "Associations" do
|
4
|
+
before do
|
5
|
+
@user = User.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "defines the method on a instance" do
|
9
|
+
@user.respond_to?(:messages).should be_true
|
10
|
+
end
|
11
|
+
|
12
|
+
it "has a destroy method" do
|
13
|
+
@user.messages.respond_to?(:destroy).should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has an add method" do
|
17
|
+
@user.messages.respond_to?(:add).should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "has a remove method" do
|
21
|
+
@user.messages.respond_to?(:remove).should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "has a get method" do
|
25
|
+
@user.messages.respond_to?(:get).should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "responds to each" do
|
29
|
+
@user.messages.respond_to?(:each).should be_true
|
30
|
+
end
|
31
|
+
|
32
|
+
it "responds to first" do
|
33
|
+
@user.messages.respond_to?(:first).should be_true
|
34
|
+
end
|
35
|
+
end
|
data/spec/index_spec.rb
ADDED
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
2
|
+
|
3
|
+
describe "CoolBreeze::Model" do
|
4
|
+
before do
|
5
|
+
@data = {
|
6
|
+
"profile_image_url" => "http://s3.amazonaws.com/twitter_production/profile_images/74591615/01-30-09_2327_normal.jpg",
|
7
|
+
"created_at" => "Sun, 22 Mar 2009 00:13:11 +0000",
|
8
|
+
"from_user" => "pbrendel",
|
9
|
+
"text" => "first time i've noticed not having digital antenna. don't get austin cbs anymore, so no horns. (internet tv to the rescue)",
|
10
|
+
"to_user_id" => nil,
|
11
|
+
"id" => 1368224751,
|
12
|
+
"from_user_id" => 4205104,
|
13
|
+
"iso_language_code" => "en",
|
14
|
+
"source" => "<a href="http://twitter.com/">web</a>"
|
15
|
+
}
|
16
|
+
end
|
17
|
+
it 'should take an optional paramter to new and set the data' do
|
18
|
+
m = Message.new(@data)
|
19
|
+
m.data.should == @data.each{|k,v| @data[k] = v.to_s}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# EOF
|
data/spec/models/user.rb
ADDED
data/spec/query_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec' # Satisfies Autotest and anyone else not using the Rake tasks
|
3
|
+
require 'redis'
|
4
|
+
require 'rufus/tokyo'
|
5
|
+
|
6
|
+
|
7
|
+
require File.expand_path(
|
8
|
+
File.join(File.dirname(__FILE__), %w[.. lib cool_breeze]))
|
9
|
+
|
10
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__), 'models','*'))].each{|m| require m}
|
11
|
+
|
12
|
+
Spec::Runner.configure do |config|
|
13
|
+
CoolBreeze::Connections.setup(:redis, Redis.new)
|
14
|
+
CoolBreeze::Connections.setup(:tokyo, Rufus::Tokyo::Table.new('log/test.tct'))
|
15
|
+
# == Mock Framework
|
16
|
+
#
|
17
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
18
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
19
|
+
#
|
20
|
+
# config.mock_with :mocha
|
21
|
+
# config.mock_with :flexmock
|
22
|
+
# config.mock_with :rr
|
23
|
+
end
|
24
|
+
|
25
|
+
# EOF
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: BrianTheCoder-cool_breeze
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- brianthecoder
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-15 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: wbsmith83@gmail.com
|
18
|
+
executables:
|
19
|
+
- cool_breeze
|
20
|
+
- redis.conf
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- README.txt
|
25
|
+
files:
|
26
|
+
- History.txt
|
27
|
+
- README.txt
|
28
|
+
- Rakefile
|
29
|
+
- VERSION.yml
|
30
|
+
- bin/cool_breeze
|
31
|
+
- bin/redis.conf
|
32
|
+
- lib/cool_breeze.rb
|
33
|
+
- lib/cool_breeze/association.rb
|
34
|
+
- lib/cool_breeze/conditions.rb
|
35
|
+
- lib/cool_breeze/connections.rb
|
36
|
+
- lib/cool_breeze/index/abstract.rb
|
37
|
+
- lib/cool_breeze/index/class_index.rb
|
38
|
+
- lib/cool_breeze/index/instance_index.rb
|
39
|
+
- lib/cool_breeze/mixins/associations.rb
|
40
|
+
- lib/cool_breeze/mixins/indices.rb
|
41
|
+
- lib/cool_breeze/model.rb
|
42
|
+
- lib/cool_breeze/query.rb
|
43
|
+
- lib/cool_breeze/types/abstract.rb
|
44
|
+
- lib/cool_breeze/types/counter.rb
|
45
|
+
- lib/cool_breeze/types/list.rb
|
46
|
+
- lib/cool_breeze/types/set.rb
|
47
|
+
- lib/cool_breeze/types/value.rb
|
48
|
+
- spec/association_spec.rb
|
49
|
+
- spec/index_spec.rb
|
50
|
+
- spec/model_spec.rb
|
51
|
+
- spec/models/message.rb
|
52
|
+
- spec/models/user.rb
|
53
|
+
- spec/query_spec.rb
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://github.com/BrianTheCoder/cool_breeze
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options:
|
59
|
+
- --charset=UTF-8
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.2.0
|
78
|
+
signing_key:
|
79
|
+
specification_version: 2
|
80
|
+
summary: A new type of orm utilizing two different storage mechanisms to the best of their abilities
|
81
|
+
test_files:
|
82
|
+
- spec/association_spec.rb
|
83
|
+
- spec/index_spec.rb
|
84
|
+
- spec/model_spec.rb
|
85
|
+
- spec/models/message.rb
|
86
|
+
- spec/models/user.rb
|
87
|
+
- spec/query_spec.rb
|
88
|
+
- spec/spec_helper.rb
|