diff options
Diffstat (limited to 'app/app/db/model.rb')
-rw-r--r-- | app/app/db/model.rb | 235 |
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 |