Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/app/app/db/model.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/app/db/model.rb')
-rw-r--r--app/app/db/model.rb235
1 files changed, 235 insertions, 0 deletions
diff --git a/app/app/db/model.rb b/app/app/db/model.rb
new file mode 100644
index 0000000..db1b2c9
--- /dev/null
+++ b/app/app/db/model.rb
@@ -0,0 +1,235 @@
+
+module HH::Sequel
+ class Model
+ @@db = nil
+
+ def self.db; @@db; end
+ def self.db=(db); @@db = db; end
+
+ def self.table_name; @table_name; end
+ def self.set_table_name(t); @table_name = t; end
+
+ def self.dataset
+ return @dataset if @dataset
+ if !table_name
+ raise RuntimeError, "Table name not specified for class #{self}."
+ elsif !db
+ raise RuntimeError, "No database connected."
+ end
+ @dataset = db[table_name]
+ @dataset.record_class = self
+ @dataset
+ end
+ def self.set_dataset(ds); @dataset = ds; @dataset.record_class = self; end
+
+ def self.cache_by(column, expiration)
+ @cache_column = column
+
+ prefix = "#{name}.#{column}."
+ define_method(:cache_key) do
+ prefix + @values[column].to_s
+ end
+
+ define_method("find_by_#{column}".to_sym) do |arg|
+ key = cache_key
+ rec = CACHE[key]
+ if !rec
+ rec = find(column => arg)
+ CACHE.set(key, rec, expiration)
+ end
+ rec
+ end
+
+ alias_method :delete, :delete_and_invalidate_cache
+ alias_method :set, :set_and_update_cache
+ end
+
+ def self.cache_column
+ @cache_column
+ end
+
+ def self.primary_key; @primary_key ||= :id; end
+ def self.set_primary_key(k); @primary_key = k; end
+
+ def self.schema(name = nil, &block)
+ name ||= table_name
+ @schema = Schema::Generator.new(name, &block)
+ set_table_name name
+ if @schema.primary_key_name
+ set_primary_key @schema.primary_key_name
+ end
+ end
+
+ def self.table_exists?
+ db.table_exists?(table_name)
+ end
+
+ def self.create_table
+ db.execute get_schema.create_sql
+ end
+
+ def self.drop_table
+ db.execute get_schema.drop_sql
+ end
+
+ def self.recreate_table
+ drop_table if table_exists?
+ create_table
+ end
+
+ def self.get_schema
+ @schema
+ end
+
+ ONE_TO_ONE_PROC = "proc {i = @values[:%s]; %s[i] if i}".freeze
+ ID_POSTFIX = "_id".freeze
+ FROM_DATASET = "db[%s]".freeze
+
+ def self.one_to_one(name, opts)
+ klass = opts[:class] ? opts[:class] : (FROM_DATASET % name.inspect)
+ key = opts[:key] || (name.to_s + ID_POSTFIX)
+ define_method name, &eval(ONE_TO_ONE_PROC % [key, klass])
+ end
+
+ ONE_TO_MANY_PROC = "proc {%s.filter(:%s => @pkey)}".freeze
+ ONE_TO_MANY_ORDER_PROC = "proc {%s.filter(:%s => @pkey).order(%s)}".freeze
+ def self.one_to_many(name, opts)
+ klass = opts[:class] ? opts[:class] :
+ (FROM_DATASET % (opts[:table] || name.inspect))
+ key = opts[:on]
+ order = opts[:order]
+ define_method name, &eval(
+ (order ? ONE_TO_MANY_ORDER_PROC : ONE_TO_MANY_PROC) %
+ [klass, key, order.inspect]
+ )
+ end
+
+ def self.get_hooks(key)
+ @hooks ||= {}
+ @hooks[key] ||= []
+ end
+
+ def self.has_hooks?(key)
+ !get_hooks(key).empty?
+ end
+
+ def run_hooks(key)
+ self.class.get_hooks(key).each {|h| instance_eval(&h)}
+ end
+
+ def self.before_delete(&block)
+ get_hooks(:before_delete).unshift(block)
+ end
+
+ def self.after_create(&block)
+ get_hooks(:after_create) << block
+ end
+
+ ############################################################################
+
+ attr_reader :values, :pkey
+
+ def model
+ self.class
+ end
+
+ def primary_key
+ model.primary_key
+ end
+
+ def initialize(values)
+ @values = values
+ @pkey = values[self.class.primary_key]
+ end
+
+ def exists?
+ model.filter(primary_key => @pkey).count == 1
+ end
+
+ def refresh
+ record = self.class.find(primary_key => @pkey)
+ record ? (@values = record.values) :
+ (raise RuntimeError, "Record not found")
+ self
+ end
+
+ def self.find(cond)
+ dataset.filter(cond).first # || (raise RuntimeError, "Record not found.")
+ end
+
+ def self.each(&block); dataset.each(&block); end
+ def self.all; dataset.all; end
+ def self.filter(*arg); dataset.filter(*arg); end
+ def self.first; dataset.first; end
+ def self.count; dataset.count; end
+ def self.map(column); dataset.map(column); end
+ def self.hash_column(column); dataset.hash_column(primary_key, column); end
+ def self.join(*args); dataset.join(*args); end
+ def self.lock(mode, &block); dataset.lock(mode, &block); end
+ def self.delete_all
+ if has_hooks?(:before_delete)
+ db.transaction {dataset.all.each {|r| r.delete}}
+ else
+ dataset.delete
+ end
+ end
+
+ def self.[](key)
+ find key.is_a?(Hash) ? key : {primary_key => key}
+ end
+
+ def self.create(values = nil)
+ db.transaction do
+ obj = find(primary_key => dataset.insert(values))
+ obj.run_hooks(:after_create)
+ obj
+ end
+ end
+
+ def delete
+ db.transaction do
+ run_hooks(:before_delete)
+ model.dataset.filter(primary_key => @pkey).delete
+ end
+ end
+
+ FIND_BY_REGEXP = /^find_by_(.*)/.freeze
+ FILTER_BY_REGEXP = /^filter_by_(.*)/.freeze
+
+ def self.method_missing(m, *args)
+ Thread.exclusive do
+ method_name = m.to_s
+ if method_name =~ FIND_BY_REGEXP
+ c = $1
+ meta_def(method_name) {|arg| find(c => arg)}
+ send(m, *args) if respond_to?(m)
+ elsif method_name =~ FILTER_BY_REGEXP
+ c = $1
+ meta_def(method_name) {|arg| filter(c => arg)}
+ send(m, *args) if respond_to?(m)
+ else
+ super
+ end
+ end
+ end
+
+ def db; @@db; end
+
+ def [](field); @values[field]; end
+
+ def ==(obj)
+ (obj.class == model) && (obj.pkey == @pkey)
+ end
+
+ def set(values)
+ model.dataset.filter(primary_key => @pkey).update(values)
+ @values.merge!(values)
+ end
+ end
+
+ def self.Model(table_name)
+ Class.new(Sequel::Model) do
+ meta_def(:inherited) {|c| c.set_table_name(table_name)}
+ end
+ end
+end