class LazyRecord

Constants

AssociationNotFound
RecordNotFound

Public Class Methods

all() click to toggle source
# 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
all_attributes() click to toggle source
# File lib/lazy_record.rb, line 43
def self.all_attributes
  @all_attributes ||= begin
    attrs = @attributes.dup
    attrs.unshift primary_key
  end
end
attributes() click to toggle source
# File lib/lazy_record.rb, line 39
def self.attributes
  @attributes ||= []
end
belongs_to_attributes() click to toggle source
# File lib/lazy_record.rb, line 54
def self.belongs_to_attributes
  @belongs_to_attributes ||= []
end
db_try() { || ... } click to toggle source
# 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
find(id) click to toggle source

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
find_by(hash, options={}) click to toggle source
# 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
inherited(base) click to toggle source
# File lib/lazy_record.rb, line 14
def self.inherited(base)
  base.class_eval do
    cattr_accessor :primary_key
    attr_reader :errors
  end
end
method_missing(method, *args, &block) click to toggle source
Calls superclass method
# 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
nested_attributes() click to toggle source
# File lib/lazy_record.rb, line 50
def self.nested_attributes
  @nested_attributes ||= []
end
new(params=nil) click to toggle source

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
tbl_attr_accessor(*fields) click to toggle source

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

assoc_table_name=( tblname=self.name.tableize ) click to toggle source

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
attr_primary(*fields) click to toggle source
# 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
belongs_to(*models) click to toggle source

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
build_from(resultset, options={}) click to toggle source

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
has_many(model, options={}) click to toggle source

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
Also aliased as: has_one
has_one(model, options={})

has_one has the same implementation as has_many

Alias for: has_many
test_resultset(res) click to toggle source

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

attributes(options={}) click to toggle source

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
build_associated(tbl) click to toggle source

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
build_from_params!(params) click to toggle source

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
destroy(options={}) click to toggle source

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() click to toggle source

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_attributes(params, extra_where={}) click to toggle source

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