blendris 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2010-02-06
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,32 @@
1
+ History.txt
2
+ Manifest
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ autotest/discover.rb
7
+ lib/blendris.rb
8
+ lib/blendris/accessor.rb
9
+ lib/blendris/errors.rb
10
+ lib/blendris/integer.rb
11
+ lib/blendris/list.rb
12
+ lib/blendris/model.rb
13
+ lib/blendris/node.rb
14
+ lib/blendris/reference.rb
15
+ lib/blendris/reference_base.rb
16
+ lib/blendris/reference_set.rb
17
+ lib/blendris/set.rb
18
+ lib/blendris/string.rb
19
+ lib/blendris/types.rb
20
+ lib/blendris/utils.rb
21
+ script/console
22
+ script/destroy
23
+ script/generate
24
+ spec/list_spec.rb
25
+ spec/model_spec.rb
26
+ spec/redis-tools_spec.rb
27
+ spec/ref_spec.rb
28
+ spec/set_spec.rb
29
+ spec/spec.opts
30
+ spec/spec_helper.rb
31
+ spec/string_spec.rb
32
+ tasks/rspec.rake
@@ -0,0 +1 @@
1
+ For more information on blendris, see http://github.com/alexmchale/blendris
@@ -0,0 +1,79 @@
1
+ = blendris
2
+
3
+ * http://github.com/alexmchale/blendris
4
+
5
+ == DESCRIPTION:
6
+
7
+ Blendris is a Ruby interface to a Redis database.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ == SYNOPSIS:
12
+
13
+ == REQUIREMENTS:
14
+
15
+ * Blendris uses the redis RubyGem.
16
+
17
+ == INSTALL:
18
+
19
+ * gem install blendris
20
+
21
+ == EXAMPLES:
22
+
23
+ The following would create a Website model that knows its url and
24
+ paths within the website.
25
+
26
+ class Website < Blendris::Model
27
+ key "website", :title
28
+
29
+ string :title
30
+ string :url
31
+ set :paths
32
+ end
33
+
34
+ website = Website.create("One Fake Website")
35
+ website.url = "http://fakewebsite.com"
36
+ website.paths << "/blog/index"
37
+ website.paths << "/admin/index"
38
+
39
+ The above would create the following Redis keys:
40
+
41
+ website:One_Fake_Website => "Website" (This identifies the model type)
42
+ website:One_Fake_Website:name => "One Fake Website"
43
+ website:One_Fake_Website:url => "http://fakewebsite.com"
44
+ website:One_Fake_Website:paths => [ "/blog/index", "/admin/index" ]
45
+
46
+ Now suppose we want to open the Website model back up to add a concept of sister sites:
47
+
48
+ class Website
49
+ refs :sister_sites, :class => Website, :reverse => :sister_sites
50
+ end
51
+
52
+ This will cause the website to maintain a set of other websites. The reverse tag
53
+ causes the other website's sister_sites set to be updated when it is added or removed
54
+ from this site's list.
55
+
56
+ == LICENSE:
57
+
58
+ (The MIT License)
59
+
60
+ Copyright (c) 2010 Alexander Timothy McHale
61
+
62
+ Permission is hereby granted, free of charge, to any person obtaining
63
+ a copy of this software and associated documentation files (the
64
+ 'Software'), to deal in the Software without restriction, including
65
+ without limitation the rights to use, copy, modify, merge, publish,
66
+ distribute, sublicense, and/or sell copies of the Software, and to
67
+ permit persons to whom the Software is furnished to do so, subject to
68
+ the following conditions:
69
+
70
+ The above copyright notice and this permission notice shall be
71
+ included in all copies or substantial portions of the Software.
72
+
73
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
74
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
75
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
76
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
77
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
78
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
79
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ require "rubygems"
2
+
3
+ gem "echoe", ">= 4.1"
4
+ gem "redis", ">= 0.1.2"
5
+
6
+ require "echoe"
7
+
8
+ require 'fileutils'
9
+ require './lib/blendris'
10
+
11
+ Echoe.new("blendris", "0.0.1") do |p|
12
+
13
+ p.description = "A redis library for Ruby"
14
+ p.url = "http://github.com/alexmchale/blendris"
15
+ p.author = "Alex McHale"
16
+ p.email = "alexmchale@gmail.com"
17
+ p.ignore_pattern = [ "tmp", "pkg", "script" ]
18
+ p.development_dependencies = []
19
+
20
+ end
21
+
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
@@ -0,0 +1,3 @@
1
+ Autotest.add_discovery do
2
+ "rspec"
3
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{blendris}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Alex McHale"]
9
+ s.cert_chain = ["/Users/amchale/Dropbox/Security/gem-public_cert.pem"]
10
+ s.date = %q{2010-02-11}
11
+ s.description = %q{A redis library for Ruby}
12
+ s.email = %q{alexmchale@gmail.com}
13
+ s.extra_rdoc_files = ["README.rdoc", "lib/blendris.rb", "lib/blendris/accessor.rb", "lib/blendris/errors.rb", "lib/blendris/integer.rb", "lib/blendris/list.rb", "lib/blendris/model.rb", "lib/blendris/node.rb", "lib/blendris/reference.rb", "lib/blendris/reference_base.rb", "lib/blendris/reference_set.rb", "lib/blendris/set.rb", "lib/blendris/string.rb", "lib/blendris/types.rb", "lib/blendris/utils.rb", "tasks/rspec.rake"]
14
+ s.files = ["History.txt", "Manifest", "PostInstall.txt", "README.rdoc", "Rakefile", "autotest/discover.rb", "lib/blendris.rb", "lib/blendris/accessor.rb", "lib/blendris/errors.rb", "lib/blendris/integer.rb", "lib/blendris/list.rb", "lib/blendris/model.rb", "lib/blendris/node.rb", "lib/blendris/reference.rb", "lib/blendris/reference_base.rb", "lib/blendris/reference_set.rb", "lib/blendris/set.rb", "lib/blendris/string.rb", "lib/blendris/types.rb", "lib/blendris/utils.rb", "script/console", "script/destroy", "script/generate", "spec/list_spec.rb", "spec/model_spec.rb", "spec/redis-tools_spec.rb", "spec/ref_spec.rb", "spec/set_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/string_spec.rb", "tasks/rspec.rake", "blendris.gemspec"]
15
+ s.homepage = %q{http://github.com/alexmchale/blendris}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Blendris", "--main", "README.rdoc"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{blendris}
19
+ s.rubygems_version = %q{1.3.5}
20
+ s.signing_key = %q{/Users/amchale/Dropbox/Security/gem-private_key.pem}
21
+ s.summary = %q{A redis library for Ruby}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ else
29
+ end
30
+ else
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Blendris
5
+ VERSION = '0.0.1'
6
+ end
7
+
8
+ require "blendris/errors"
9
+ require "blendris/utils"
10
+
11
+ require "blendris/accessor"
12
+ require "blendris/node"
13
+
14
+ require "blendris/model"
15
+
16
+ require "blendris/string"
17
+ require "blendris/integer"
18
+ require "blendris/list"
19
+ require "blendris/set"
20
+
21
+ require "blendris/reference_base"
22
+ require "blendris/reference"
23
+ require "blendris/reference_set"
24
+
25
+ require "blendris/types"
@@ -0,0 +1,62 @@
1
+ require 'redis'
2
+
3
+ module Blendris
4
+
5
+ module RedisAccessor
6
+
7
+ include Utils
8
+
9
+ def redis
10
+ RedisAccessor.redis
11
+ end
12
+
13
+ def self.redis
14
+ $_redis_connection ||= Redis.new
15
+ end
16
+
17
+ def prefix
18
+ RedisAccessor.prefix
19
+ end
20
+
21
+ def generate_key(klass, values)
22
+ value_index = 0
23
+
24
+ klass.local_parameters.map do |symbol|
25
+ case symbol
26
+
27
+ when String then symbol
28
+
29
+ when Symbol
30
+ value = values[value_index]
31
+ value_index += 1
32
+
33
+ raise ArgumentError.new("#{self.name} created without #{symbol}") unless value
34
+
35
+ klass.cast_value symbol, value
36
+
37
+ else
38
+ raise TypeError.new("only strings and symbols allowed in key definition for #{klass.name} (#{symbol.class.name})")
39
+
40
+ end
41
+ end.map do |segment|
42
+ sanitize_key segment
43
+ end.compact.join(":")
44
+ end
45
+
46
+ def self.prefix
47
+ $_redis_prefix ||= ""
48
+ end
49
+
50
+ def self.prefix=(prefix)
51
+ $_redis_prefix = prefix.to_s
52
+ end
53
+
54
+ def self.flush_keys
55
+ redis.keys("#{prefix}*").each do |key|
56
+ redis.del key
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,3 @@
1
+ module Blendris
2
+
3
+ end
@@ -0,0 +1,19 @@
1
+ module Blendris
2
+
3
+ class RedisInteger
4
+
5
+ include RedisNode
6
+
7
+ def self.cast_to_redis(value, options = {})
8
+ raise TypeError.new("#{value.class.name} is not an integer") unless value.kind_of? Fixnum
9
+
10
+ value
11
+ end
12
+
13
+ def self.cast_from_redis(value, options = {})
14
+ value.to_i if value
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,37 @@
1
+ module Blendris
2
+
3
+ class RedisList
4
+
5
+ include RedisNode
6
+ include Enumerable
7
+
8
+ def initialize(key, options = {})
9
+ @key = key.to_s
10
+ @options = options
11
+ end
12
+
13
+ def each
14
+ redis.lrange(key, 0, -1).each do |value|
15
+ yield value
16
+ end
17
+ end
18
+
19
+ def <<(value)
20
+ [ value ].flatten.compact.each do |v|
21
+ redis.rpush key, v
22
+ end
23
+
24
+ self
25
+ end
26
+
27
+ def get
28
+ self
29
+ end
30
+
31
+ def delete(value)
32
+ redis.lrem key, 0, value
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,141 @@
1
+ module Blendris
2
+
3
+ class Model
4
+
5
+ include RedisAccessor
6
+
7
+ attr_reader :key
8
+
9
+ def initialize(new_key, options = {})
10
+ @key = sanitize_key(new_key)
11
+ actual_type = constantize(redis.get(prefix + key))
12
+
13
+ raise ArgumentError.new("#{self.class.name} second argument must be a hash") unless options.kind_of? Hash
14
+ raise TypeError.new("#{prefix + key} does not exist, not a #{self.class.name} - you may want create instead of new") if !actual_type
15
+ raise TypeError.new("#{prefix + key} is a #{actual_type}, not a #{self.class.name}") if actual_type != self.class
16
+
17
+ if options[:verify] != false
18
+ parameters = self.class.local_parameters.find_all {|s| s.kind_of? Symbol}
19
+ dne = parameters.find {|p| not self.send(p.to_s)}
20
+
21
+ raise ArgumentError.new("#{self.class.name} #{key} is missing its #{dne}") if dne
22
+ raise ArgumentError.new("blank keys are not allowed") if @key.length == 0
23
+ end
24
+ end
25
+
26
+ def id
27
+ Digest::SHA1.hexdigest key
28
+ end
29
+
30
+ def method_missing(method_sym, *arguments)
31
+ (name, setter) = method_sym.to_s.scan(/(.*[^=])(=)?/).first
32
+
33
+ if node = redis_symbol(name)
34
+ if setter
35
+ return node.set *arguments
36
+ else
37
+ return node.get
38
+ end
39
+ end
40
+
41
+ super
42
+ end
43
+
44
+ def redis_symbol(name)
45
+ subkey = self.subkey(name)
46
+
47
+ options = self.class.redis_symbols[name.to_s]
48
+
49
+ return unless options
50
+
51
+ options = options.merge(:model => self)
52
+
53
+ options[:type].new subkey, options
54
+ end
55
+
56
+ def subkey(key)
57
+ sanitize_key "#{self.key}:#{key}"
58
+ end
59
+
60
+ def ==(other)
61
+ return false unless self.class == other.class
62
+ return self.key == other.key
63
+ end
64
+
65
+ class << self
66
+
67
+ include RedisAccessor
68
+
69
+ # This method will instantiate a new object with the correct key
70
+ # and assign the values passed to it.
71
+ def create(*args)
72
+ parameters = local_parameters.find_all {|s| s.kind_of? Symbol}
73
+ got = args.count
74
+ wanted = parameters.count
75
+
76
+ if got != wanted
77
+ msg = "wrong number of arguments for a new #{self.class.name} (%d for %d)" % [ got, wanted ]
78
+ raise ArgumentError.new(msg)
79
+ end
80
+
81
+ key = generate_key(self, args)
82
+ current_model = redis.get(prefix + key)
83
+
84
+ if current_model && current_model != self.name
85
+ raise ArgumentError.new("#{key} is a #{current_model}, not a #{self.name}")
86
+ end
87
+
88
+ redis.set(prefix + key, self.name)
89
+
90
+ obj = new(key, :verify => false)
91
+
92
+ parameters.each_with_index do |parm, i|
93
+ obj.redis_symbol(parm).set args[i]
94
+ end
95
+
96
+ obj
97
+ end
98
+
99
+ def key(*fields)
100
+ @local_parameters = fields
101
+
102
+ @local_parameters.flatten!
103
+ @local_parameters.compact!
104
+
105
+ nil
106
+ end
107
+
108
+ # Defines a new data type for Blendris:Model construction.
109
+ def type(name, klass)
110
+ (class << self; self; end).instance_eval do
111
+ define_method name do |varname, options|
112
+ options ||= {}
113
+ options[:type] = klass
114
+ redis_symbols[varname.to_s] = options
115
+ end
116
+ end
117
+ end
118
+
119
+ # Variables stored in the Redis database.
120
+ def redis_symbols
121
+ @redis_symbols ||= {}
122
+ end
123
+
124
+ # Parameters used when creating a new copy of this model.
125
+ def local_parameters
126
+ @local_parameters ||= []
127
+ end
128
+
129
+ # Take a value and attempt to make it fit the given field.
130
+ def cast_value(symbol, value)
131
+ options = redis_symbols[symbol.to_s]
132
+ raise ArgumentError.new("#{self.name} is missing its #{symbol}") unless options
133
+
134
+ options[:type].cast_to_redis value, options
135
+ end
136
+
137
+ end
138
+
139
+ end
140
+
141
+ end