class Knj::Datarow attr_reader :data, :ob, :db def self.required_data @required_data = [] if !@required_data return @required_data end def self.depending_data @depending_data = [] if !@depending_data return @depending_data end def is_knj?; return true; end def self.is_nullstamp?(stamp) return true if !stamp or stamp == "0000-00-00 00:00:00" or stamp == "0000-00-00" return false end def self.has_many(arr) arr.each do |val| if val.is_a?(Array) classname, colname, methodname = *val elsif val.is_a?(Hash) classname = val[:class] colname = val[:col] methodname = val[:method] if val[:depends] depending_data << { :colname => colname, :classname => classname } end else raise "Unknown argument: '#{val.class.name}'." end if val.is_a?(Hash) and val.key?(:where) where_args = val[:where] else where_args = {} end raise "No classname given." if !classname methodname = "#{classname.to_s.downcase}s".to_sym if !methodname define_method(methodname) do |*args, &block| merge_args = args[0] if args and args[0] merge_args = {} if !merge_args all_args = [] all_args << block if block if block @ob.list(classname, merge_args.merge(colname.to_s => self.id)) do |obj| block.call(obj) end else return @ob.list(classname, merge_args.merge(colname.to_s => self.id)) end end define_method("#{methodname}_count".to_sym) do |*args| merge_args = args[0] if args and args[0] merge_args = {} if !merge_args return @ob.list(classname, merge_args.merge(colname.to_s => self.id, "count" => true)) end define_method("#{methodname}_last".to_sym) do |args| args = {} if !args return @ob.list(classname, {"orderby" => [["id", "desc"]], "limit" => 1}.merge(args)) end end end def self.has_one(arr) arr.each do |val| methodname = nil colname = nil classname = nil if val.is_a?(Symbol) classname = val methodname = val.to_s.downcase.to_sym colname = "#{val.to_s.downcase}_id".to_sym elsif val.is_a?(Array) classname, colname, methodname = *val elsif val.is_a?(Hash) classname, colname, methodname = val[:class], val[:col], val[:method] if val[:required] colname = "#{classname.to_s.downcase}_id".to_sym if !colname required_data << { :col => colname, :class => classname } end else raise "Unknown argument-type: '#{arr.class.name}'." end methodname = classname.to_s.downcase if !methodname colname = "#{classname.to_s.downcase}_id".to_sym if !colname define_method(methodname) do return @ob.get_try(self, colname, classname) end methodname_html = "#{methodname.to_s}_html".to_sym define_method(methodname_html) do |*args| obj = self.send(methodname) return @ob.events.call(:no_html, classname) if !obj raise "Class '#{classname}' does not have a 'html'-method." if !obj.respond_to?(:html) return obj.html(*args) end end end def self.joined_tables(hash) @columns_joined_tables = hash end def self.table return self.name.split("::").last end def self.columns(d) columns_load(d) if !@columns return @columns end def self.columns_load(d) return nil if @columns @ob = d.ob @columns = d.db.tables[table].columns end def self.columns_sqlhelper_args return @columns_sqlhelper_args end def self.list(d) ec_col = d.db.enc_col ec_table = d.db.enc_table table_str = "#{ec_table}#{d.db.esc_table(self.table)}#{ec_table}" if d.args["count"] count = true d.args.delete("count") sql = "SELECT COUNT(#{table_str}.#{ec_col}id#{ec_col}) AS count" else sql = "SELECT #{table_str}.*" end ret = self.list_helper(d) sql += " FROM #{table_str}" sql += ret[:sql_joins] sql += " WHERE 1=1" d.args.each do |key, val| case key when "return_sql" #ignore else raise "Invalid key: '#{key}'." end end sql += ret[:sql_where] #The count will bug if there is a group-by-statement. if !count sql += " GROUP BY #{table_str}.#{ec_col}id#{ec_col}" end sql += ret[:sql_order] sql += ret[:sql_limit] return sql.to_s if d.args["return_sql"] if count ret = d.db.query(sql).fetch return ret[:count].to_i if ret return 0 end return d.ob.list_bysql(self.table, sql) end def self.load_columns(d) @mutex = Mutex.new if !@mutex @mutex.synchronize do cols = self.columns(d) inst_methods = instance_methods(false) sqlhelper_args = { :db => d.db, :table => table, :cols_bools => [], :cols_date => [], :cols_dbrows => [], :cols_num => [], :cols_str => [], :cols => {} } cols.each do |col_name, col_obj| col_type = col_obj.type col_type = "int" if col_type == "bigint" or col_type == "tinyint" or col_type == "mediumint" or col_type == "smallint" sqlhelper_args[:cols][col_name] = true if col_type == "enum" and col_obj.maxlength == "'0','1'" sqlhelper_args[:cols_bools] << col_name method_name = "#{col_name}?".to_sym if !inst_methods.index(method_name) define_method(method_name) do return true if self[col_name.to_sym].to_s == "1" return false end end elsif col_type == "int" and col_name.slice(-3, 3) == "_id" sqlhelper_args[:cols_dbrows] << col_name elsif col_type == "int" or col_type == "bigint" sqlhelper_args[:cols_num] << col_name elsif col_type == "varchar" or col_type == "text" or col_type == "enum" sqlhelper_args[:cols_str] << col_name elsif col_type == "date" or col_type == "datetime" sqlhelper_args[:cols_date] << col_name method_name = "#{col_name}_str".to_sym if !inst_methods.index(method_name) define_method(method_name) do |*args| if Knj::Datet.is_nullstamp?(self[col_name.to_sym]) return @ob.events.call(:no_date, self.class.name) end return Knj::Datet.in(self[col_name.to_sym]).out(*args) end end method_name = "#{col_name}".to_sym if !inst_methods.index(method_name) define_method(method_name) do |*args| return false if Knj::Datet.is_nullstamp?(self[col_name.to_sym]) return Knj::Datet.in(self[col_name.to_sym]) end end end if col_type == "int" or col_type == "decimal" method_name = "#{col_name}_format" if inst_methods.index(method_name) == nil define_method(method_name) do |*args| return Knj::Locales.number_out(self[col_name.to_sym], *args) end end end if col_type == "int" or col_type == "varchar" method_name = "by_#{col_name}".to_sym if !inst_methods.index(method_name) and RUBY_VERSION.to_s.slice(0, 3) != "1.8" define_singleton_method(method_name) do |arg| return d.ob.get_by(self.table, {col_name.to_s => arg}) end end end if col_type == "time" method_name = "#{col_name}_dbt" if !inst_methods.index(method_name) define_method(method_name) do return Knj::Db::Dbtime.new(self[col_name.to_sym]) end end end end if @columns_joined_tables @columns_joined_tables.each do |table_name, table_data| table_data[:where].each do |key, val| val[:table] = self.table.to_sym if val.is_a?(Hash) and !val.key?(:table) and val[:type] == "col" end table_data[:datarow] = @ob.args[:module].const_get(table_name.to_sym) if !table_data.key?(:datarow) end sqlhelper_args[:joined_tables] = @columns_joined_tables end @columns_sqlhelper_args = sqlhelper_args end self.init_class(d) if self.respond_to?(:init_class) end def self.list_helper(d) self.load_columns(d) if !@columns_sqlhelper_args return d.ob.sqlhelper(d.args, @columns_sqlhelper_args) end def table return self.class.name.split("::").last end def initialize(d) @ob = d.ob @db = d.ob.db raise "No ob given." if !@ob if d.data.is_a?(Hash) @data = d.data elsif d.data @data = {:id => d.data} self.reload else raise Knj::Errors::InvalidData, "Could not figure out the data from '#{d.data.class.name}'." end end def reload data = @db.single(self.table, {:id => @data[:id]}) if !data raise Knj::Errors::NotFound, "Could not find any data for the object with ID: '#{@data[:id]}' in the table '#{self.table}'." end @data = data end def update(newdata) @db.update(self.table, newdata, {:id => @data[:id]}) self.reload if @ob @ob.call("object" => self, "signal" => "update") end end def destroy @ob = nil @db = nil @data = nil end def has_key?(key) return @data.key?(key.to_sym) end def key?(key) return @data.key?(key.to_sym) end def [](key) raise "No valid key given." if !key.is_a?(Symbol) raise "No data was loaded on the object? Maybe you are trying to call a deleted object?" if !@data return @data[key] if @data.key?(key) raise "No such key: #{key}." end def []=(key, value) self.update(key.to_sym => value) self.reload end def id raise "No data on object." if !@data return @data[:id] end def name if @data.key?(:title) return @data[:title] elsif @data.key?(:name) return @data[:name] end obj_methods = self.class.instance_methods(false) [:name, :title].each do |method_name| return self.method(method_name).call if obj_methods.index(method_name) end raise "Couldnt figure out the title/name of the object on class #{self.class.name}." end alias :title :name def each(&args) return @data.each(&args) end end