class LazyRecord
Constants
- AssociationNotFound
- RecordNotFound
Public Class Methods
# File lib/lazy_record.rb, line 334 def self.all sql = "SELECT * FROM #{self.table_name}" res = nil db_try do res = Db.conn.execute(sql) end build_from res, :always_return_array => true end
# File lib/lazy_record.rb, line 43 def self.all_attributes @all_attributes ||= begin attrs = @attributes.dup attrs.unshift primary_key end end
# File lib/lazy_record.rb, line 39 def self.attributes @attributes ||= [] end
# File lib/lazy_record.rb, line 54 def self.belongs_to_attributes @belongs_to_attributes ||= [] end
# File lib/lazy_record.rb, line 283 def self.db_try begin yield rescue DBI::DatabaseError @retries ||= 0; @retries += 1 if @retries == 1 load 'config/db_connect.rb' retry else raise end end end
id is the primary key of the table, and does not need to be named 'id' in the table itself TODO take into account other dbms's, this only works w/ mysql
# File lib/lazy_record.rb, line 301 def self.find(id) sql = "SELECT * FROM #{self.table_name} WHERE #{self.primary_key} = ?" res = nil db_try do res = Db.conn.execute sql, id end build_from res end
# File lib/lazy_record.rb, line 310 def self.find_by(hash, options={}) opts = {:conjunction => 'AND'}.merge options conj = opts[:conjunction] sql = "SELECT * FROM #{self.table_name} WHERE " values = [] hash.each do |k, v| sql += "#{k} = ? #{conj} " values << v end case conj when 'AND' sql = sql[0...-4] when 'OR' sql = sql[0...-3] else raise "conjunction in sql condition (WHERE) must be one of AND, OR" end res = nil db_try do res = Db.conn.execute sql, *values end build_from res end
# File lib/lazy_record.rb, line 14 def self.inherited(base) base.class_eval do cattr_accessor :primary_key attr_reader :errors end end
# File lib/lazy_record.rb, line 371 def method_missing(method, *args, &block) if method =~ %r{find_by_(.*)} h_args = {$1 => args[0]} return __send__ :find_by, h_args, &block end if method =~ %r{table_name} return __send__ :assoc_table_name= end super end
# File lib/lazy_record.rb, line 50 def self.nested_attributes @nested_attributes ||= [] end
if parameters are given, builds the model object with the attributes from the given parameters
# File lib/lazy_record.rb, line 24 def initialize(params=nil) unless params.nil? self.build_from_params!(params) end @errors = ActiveModel::Errors.new(self) end
used to keep track of all table attributes
# File lib/lazy_record.rb, line 32 def self.tbl_attr_accessor *fields fields.each do |f| self.attributes << f.to_s unless self.attributes.include? f.to_s end self.__send__ :attr_accessor, *fields end
Private Class Methods
associated table name, by default is just to add an 's' to the model name
# File lib/lazy_record.rb, line 140 def self.assoc_table_name=( tblname=self.name.tableize ) self.__send__ :cattr_accessor, :table_name self.table_name = tblname.to_s end
# File lib/lazy_record.rb, line 125 def self.attr_primary(*fields) if fields.length == 1 self.primary_key = fields[0].to_s != "" ? fields[0].to_s : nil else self.primary_key = fields.map {|f| f.to_s } end class_eval do fields.each do |f| attr_accessor f unless f.nil? end end end
barely does anything, just works with has_many
# File lib/lazy_record.rb, line 119 def self.belongs_to *models models.each do |m| self.belongs_to_attributes << m.to_s end end
meant for internal use
# File lib/lazy_record.rb, line 346 def self.build_from(resultset, options={}) opts = {:always_return_array => false}.merge options test_resultset resultset model_instances = [].tap do |m| resultset.fetch_hash do |h| model = self.new model.build_from_params! h m << model end resultset.finish end model_instances.length == 1 && !opts[:always_return_array] ? model_instances[0] : model_instances end
Defines the accessor(s) and makes sure the other model includes an appropriate association as well.
The name of the model can be made explicit if it's different from the default (chop the trailing 's' off from the model name).
Example: has_many
:tags, {:through => 'tags_articles', :fk => 'tagid'}
default
¶ ↑
has_many
:tags
is equivalent to
has_many
:tags, {:through => 'tags_articles', :fk => 'tag_id'}
# File lib/lazy_record.rb, line 77 def self.has_many model, options={} # @join_tables: # Used with LR#build_associated, to find out what the join table and # the foreign key are. If not found, it generates default ones by the # heuristic explained above the LR#build_associated method declaration. @join_tables ||= {} unless @join_tables[model] @join_tables[model] = {} end if through = options[:through] @join_tables[model][:through] = through end if fk = options[:fk] @join_tables[model][:fk] = fk end model_string = model.to_s.singularize self.require_model model_string model_klass = Object.const_get model_string.camelize ivar_name = model # Make sure associated model #belongs_to this class. unless model_klass.belongs_to_attributes.include? self.name. tableize raise AssociationNotFound.new "#{model_klass} doesn't #belong_to " \ "#{self.name}" end class_eval do define_method ivar_name do instance_variable_get("@#{ivar_name}") || [] end attr_writer ivar_name self.nested_attributes << ivar_name end end
meant for internal use
# File lib/lazy_record.rb, line 362 def self.test_resultset(res) if res.blank? raise RecordNotFound.new "Bad resultset #{res}" end end
Public Instance Methods
Have to implement Model#attributes to play nice with ActiveModel serializers
# File lib/lazy_record.rb, line 156 def attributes(options={}) opts = {:include_pk => true}.merge options if opts[:include_pk] attrs = self.class.all_attributes else attrs = self.class.attributes end {}.tap do |h| attrs.each do |a_name| h[a_name] = instance_variable_get "@#{a_name}" end end end
This method uses the model instance's class::has_many() method to determine what the join table is called. The default join table name that this method uses if no options were given to Model::has_many() (see Model::has_many() for the passable options) is the following:
the tbl argument (for example, :tags), concatenated with `self`'s class's table name (for example, 'articles') to make 'tags_articles'
The default foreign key uses a similar heuristic. For the example above, it would be 'tag_id', because the given table name is 'tags', and the singular is 'tag'. This is then concatenated with '_id'
To override the defaults, provide options to Model::has_many()
# File lib/lazy_record.rb, line 247 def build_associated tbl self_tbl = self.class.table_name through = if _through = self.class. instance_variable_get("@join_tables")[tbl][:through] _through else "#{tbl}_#{self_tbl}" end fk = if _fk = self.class. instance_variable_get("@join_tables")[tbl][:fk] _fk else "#{tbl.to_s.singularize}_id" end tbl_model_name = tbl.to_s.singularize.camelize begin tbl_model = Object.const_get tbl_model_name rescue NameError retry if require "app/models/#{tbl_model_name.downcase}" end pk = self.class.primary_key id = __send__ pk sql = "SELECT * FROM #{tbl} INNER JOIN #{through} ON #{tbl}.#{tbl_model.primary_key} = " \ "#{through}.#{fk} WHERE #{through}.#{self_tbl.singularize + '_id'} = ?" puts sql res = nil self.class.db_try do res = Db.conn.execute sql, id end objs = tbl_model.build_from res p objs objs = Array.wrap(objs) unless Array === objs __send__("#{tbl}=", __send__(tbl) + objs) unless objs.blank? end
meant for internal use
# File lib/lazy_record.rb, line 148 def build_from_params!(params) params.each do |k, v| self.__send__("#{k}=".intern, v) end end
delete the current model instance from the database
# File lib/lazy_record.rb, line 194 def destroy(options={}) if options[:where] else sql = "DELETE FROM #{self.class.table_name} WHERE " \ "#{self.primary_key} = ?" result = nil self.class.db_try do result = Db.conn.execute sql, instance_variable_get("@#{self.primary_key}") end result end end
save current model instance into database
# File lib/lazy_record.rb, line 171 def save return unless valid? sql = "INSERT INTO #{self.class.table_name} (" fields = self.class.attributes sql += fields.join(', ') + ') VALUES (' fields.each {|f| sql += '?, '} sql = sql[0...-2] + ')' values = fields.map do |f| ivar = instance_variable_get "@#{f}" if ivar.nil? "NULL" else ivar end end result = nil self.class.db_try do result = Db.conn.execute sql, *values end result ? true : false end
update the current model instance in the database
# File lib/lazy_record.rb, line 209 def update_attributes(params, extra_where={}) values = [] key = self.primary_key id = params.delete key if extra_where.blank? sql = "UPDATE #{self.class.table_name} SET " params.each do |k,v| sql += "#{k} = ?, " values << v end sql = sql[0...-2] sql += " WHERE #{key} = ?" values << id else end res = nil self.class.db_try do res = Db.conn.execute sql, *values end res end