Permalink
Browse files

testing basic field creation

  • Loading branch information...
1 parent 620c514 commit 2c565fa485606077dfb584a22224586adefd67ab @BrianTheCoder committed Oct 31, 2009
View
@@ -19,3 +19,5 @@ rdoc
pkg
## PROJECT::SPECIFIC
+.gitignore
+tmp/*
View
@@ -5,12 +5,14 @@ begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "redis-types"
- gem.summary = %Q{TODO: one-line summary of your gem}
- gem.description = %Q{TODO: longer description of your gem}
+ gem.summary = %Q{TODO: a port of redis-model to a module for composition}
+ gem.description = %Q{TODO: this is my port of redis-module because I prefer composition over inheritance, cause then you can combine it with other orms. I also changed json parsing to yajl-ruby and tweaked a few other things to my preferences}
gem.email = "wbsmith83@gmail.com"
gem.homepage = "http://github.com/BrianTheCoder/redis-types"
gem.authors = ["brianthecoder"]
- gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
+ gem.add_development_dependency "rspec", ">= 0"
+ gem.add_dependency "redis-rb"
+ gem.add_dependency "yajl-ruby"
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
View
9 TODO
@@ -0,0 +1,9 @@
+* implement not instant persistence
+ * save method
+ * create method
+ * have setters write to attrs hash
+ * getters
+ * dirty?
+* method of getting id(don't have to assign one)
+* figure out how to extend a class with a class OR use Redis::DataTypes::Class for marshal
+* invert values so you can find by them i.e. "user:1:name" is value, "user:name:value" is id
View
@@ -0,0 +1,42 @@
+require 'rubygems'
+$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
+require 'redis/types'
+
+
+class User
+ include Redis::Types
+
+ value :name, String
+ value :created, DateTime
+ value :profile, Json
+
+ list :posts, Json
+
+ set :followers, Integer
+end
+
+
+
+u = User.new(:id => 1)
+# u.destroy
+u.name = 'Joe'
+u.created = DateTime.now
+u.profile = {
+ :age => 23,
+ :sex => 'M',
+ :about => 'Lorem'
+}
+u.posts << {
+ :title => "Hello world!",
+ :text => "lorem"
+}
+u.followers << 2
+
+
+
+u = User.new(:id => 1)
+p u.name
+p u.created.strftime('%m/%d/%Y')
+p u.posts[0,20]
+p u.posts[0]
+p u.followers.has_key?(2)
View
No changes.
View
@@ -0,0 +1,39 @@
+class Redis
+ class DataTypes
+ class String
+ def self.dump(value); value; end
+ def self.load(value); value; end
+ end
+
+ class Integer
+ def self.dump(value); value.to_s; end
+ def self.load(value); value && value.to_i; end
+ end
+
+ class Float
+ def self.dump(value); value.to_s; end
+ def self.load(value); value && value.to_f; end
+ end
+
+ class EpochTime < Time
+ def self.dump(value)
+ value.is_a?(Integer) ? Time.at(value) : value
+ end
+
+ def self.load(value)
+ return value.to_time.to_i if value.is_a? DateTime
+ value.to_i
+ end
+ end
+
+ class DateTime
+ def self.dump(value); value.strftime('%FT%T%z'); end
+ def self.load(value); value && ::DateTime.strptime(value, '%FT%T%z'); end
+ end
+
+ class Json
+ def self.dump(value); Yajl::Encoder.encode(value); end
+ def self.load(value); v && Yajl::Parser.parse(value); end
+ end
+ end
+end
View
@@ -0,0 +1,21 @@
+class Redis
+ module FieldProxy #:nodoc
+ attr_accessor :redis, :key, :marshal
+
+ def initialize(redis, marshal)
+ @redis = redis
+ @key = key
+ @marshal = marshal
+ end
+
+ def method_missing(method, *argv)
+ translated_method = translate_method_name(method)
+ raise NoMethodError.new("Method '#{method}' is not defined") unless translated_method
+ @redis.send translated_method, @key, *argv
+ end
+
+ protected
+
+ def translate_method_name(m); m; end
+ end
+end
View
@@ -0,0 +1,45 @@
+class Redis::List
+ include Redis::FieldProxy #:nodoc:
+ def <<(v)
+ @redis.rpush @key, @marshal.dump(v)
+ end
+
+ def push_head(v); @redis.lpush @key, @marshal.dump(v); end
+
+ def pop_tail; @marshal.load(@redis.rpop(@key)); end
+
+ def pop_head; @marshal.load(@redis.lpop(@key)); end
+
+ def [](from, to = nil)
+ if to.nil?
+ @marshal.load(@redis.lindex(@key, from))
+ else
+ @redis.lrange(@key, from, to).map! { |v| @marshal.load(v) }
+ end
+ end
+
+ def []=(index, v); @redis.lset(@key, index, @marshal.dump(v)); end
+
+
+ alias_method :range, :[]
+ alias_method :push_tail, :<<
+ alias_method :set, :[]=
+
+ def include?(v); @redis.exists(@key, @marshal.dump(v)); end
+
+ def remove(count, v); @redis.lrem(@key, count, @marshal.dump(v)); end
+
+ def length; @redis.llen(@key); end
+
+ def trim(from, to); @redis.ltrim(@key, from, to); end
+
+ def to_s; range(0, 100).join(', '); end
+
+ def set(value)
+
+ end
+
+ protected
+
+ def translate_method_key(m); COMMANDS[m]; end
+end
View
@@ -0,0 +1,48 @@
+class Redis::Set < Set
+ include Redis::FieldProxy #:nodoc:
+ COMMANDS = {
+ :intersect_store => "sinterstore",
+ :union_store => "sunionstore",
+ :diff_store => "sdiffstore",
+ :move => "smove",
+ }
+
+ def <<(v); @redis.sadd @key, @marshal.dump(v); end
+
+ def delete(v); @redis.srem @key, @marshal.dump(v); end
+
+ def include?(v); @redis.sismember @key, @marshal.dump(v); end
+
+ alias_method :add, :<<
+ alias_method :remove, :delete
+ alias_method :has_key?, :include?
+ alias_method :member?, :include?
+
+ def members
+ @redis.smembers(@key).map { |v| @marshal.load(v) }
+ end
+
+ def intersect(*keys)
+ @redis.sinter(@key, *keys).map { |v| @marshal.load(v) }
+ end
+
+ def union(*keys)
+ @redis.sunion(@key, *keys).map { |v| @marshal.load(v) }
+ end
+
+ def diff(*keys)
+ @redis.sdiff(@key, *keys).map { |v| @marshal.load(v) }
+ end
+
+ def length; @redis.llen(@key); end
+
+ def to_s; members.join(', '); end
+
+ def set(value)
+
+ end
+
+ protected
+
+ def translate_method_name(m); COMMANDS[m]; end
+end
View
@@ -0,0 +1,129 @@
+require 'redis'
+require 'set'
+require 'yajl'
+require 'extlib'
+require 'pathname'
+
+class Redis
+ dir = Pathname(__FILE__).dirname.expand_path.to_s
+
+ autoload :FieldProxy, dir / 'field_proxy'
+ autoload :Set, dir / 'set'
+ autoload :List, dir / 'list'
+ autoload :Value, dir / 'value'
+ autoload :DataTypes, dir / 'data_types'
+
+ module Types
+ def self.included(model)
+ extend_core_types
+ model.send :include, Extlib::Hook
+ model.extend ClassMethods
+ end
+
+ def self.extend_core_types
+
+ end
+
+ module ClassMethods
+ attr_accessor :prefix
+
+ def redis_fields; @@_redis_fields ||= {}; end
+
+ def set(name, type = 'String', opts = {})
+ redis_field(name, type, ::Redis::Set)
+ end
+
+ def list(name, type = 'String', opts = {})
+ redis_field(name, type, ::Redis::List)
+ end
+
+ def value(name, type = 'String', opts = {})
+ redis_field(name, type, ::Redis::Value)
+ end
+
+ def redis(opts = {}); @@_redis ||= Redis.new(opts); end
+
+ def delete(id); self.find(id).destroy; end
+
+ def find(id); self.new(:id => id); end
+
+ def const_missing(name)
+ return Redis::DataTypes.const_get(name) if Redis::DataTypes.const_defined?(name)
+ super
+ end
+
+ private
+
+ def redis_field(name, type, redis_type)
+ p type
+ redis_fields[name.to_s] = redis_type.new(redis, type)
+ field_methods name, redis_type.name.split('::').last.downcase
+ end
+
+ def field_methods(name, type) #:nodoc:
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def redis_#{type}_#{name}
+ self.class.redis_fields['#{name}'].tap{|type| type.key = field_key('#{name}') }
+ end
+ RUBY
+ define_getter name, type
+ define_bool name, type
+ define_setter name, type
+ end
+
+ def define_getter(name, type) #:nodoc:
+ class_eval "def #{name}; redis_#{type}_#{name}; end"
+ end
+
+ def define_setter(name, type) #:nodoc:
+ class_eval "def #{name}=(value); redis_#{type}_#{name}.set(value); end"
+ end
+
+ def define_bool(name, type) #:nodoc:
+ class_eval "def #{name}?; redis_#{type}_#{name}.blank?; end"
+ end
+ end
+
+ attr_accessor :id
+
+ def initialize(attrs = {})
+ attrs.each do |name, value|
+ self[name] = value
+ end
+ end
+
+ def field_key(name); "#{prefix}:#{id}:#{name}"; end
+
+ def prefix #:nodoc:
+ @prefix ||= self.class.prefix || self.class.to_s.
+ sub(%r{(.*::)}, '').
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ downcase
+ end
+
+ def redis(opts = {}) #:nodoc:
+ self.class.redis(opts)
+ end
+
+ def [](method)
+ raise NoMethodError, "method is undefined: #{method}" unless respond_to?(method)
+ __send__(method)
+ end
+
+ def []=(method, value)
+ raise NoMethodError, "no setter method defined: #{method}=" unless respond_to?(:"#{method}=")
+ __send__(:"#{method}=", value)
+ end
+
+ def destroy(name = nil)
+ if name
+ redis.delete field_key(name.to_s)
+ else
+ self.class.redis_fields.each do |field|
+ redis.delete field_key(field)
+ end
+ end
+ end
+ end
+end
View
@@ -0,0 +1,3 @@
+class Redis::Value
+ include Redis::FieldProxy
+end
View
Binary file not shown.
Oops, something went wrong.

0 comments on commit 2c565fa

Please sign in to comment.