Permalink
Browse files

More optimization of Model#initialize

This should make creating model objects a little faster.  Instead of
always adding the @associations and @changed_columns instance
variables, lazily load them.

This commit also fixes Model#refresh to clear the changed columns as
well as the associations.

This potentially breaks backwards compatibility if you were
assuming the instance variables were always present.  Relying on
instance variable behavior is a very bad idea, though.
  • Loading branch information...
1 parent 159130e commit 22fdc4228d7fbb4cb7fc30a7b8e0c7f1d9e876c1 @jeremyevans committed Nov 13, 2008
Showing with 34 additions and 29 deletions.
  1. +2 −0 CHANGELOG
  2. +1 −1 lib/sequel_model/associations.rb
  3. +31 −28 lib/sequel_model/record.rb
View
@@ -1,5 +1,7 @@
=== HEAD
+* More optimization of Model#initialize (jeremyevans)
+
* Treat interval as a string type, not an integer type (jeremyevans)
* Allow use of implicitly qualified symbol as argument to Symbol#qualify (:a.qualify(:b__c)=>b.c.a), fixes model associations in different schemas (jeremyevans) (#246)
@@ -394,7 +394,7 @@ def def_many_to_one(opts)
return if old_val and run_association_callbacks(opts, :before_remove, old_val) == false
return if o and run_association_callbacks(opts, :before_add, o) == false
send(opts._setter_method, o)
- @associations[name] = o
+ associations[name] = o
remove_reciprocal_object(opts, old_val) if old_val
add_reciprocal_object(opts, o) if o
run_association_callbacks(opts, :after_add, o) if o
View
@@ -4,15 +4,6 @@ class Model
# to be called automatically via set.
RESTRICTED_SETTER_METHODS = %w"== === []= taguri= typecast_empty_string_to_nil= typecast_on_assignment= strict_param_setting= raise_on_save_failure= raise_on_typecast_failure="
- # The current cached associations. A hash with the keys being the
- # association name symbols and the values being the associated object
- # or nil (many_to_one), or the array of associated objects (*_to_many).
- attr_reader :associations
-
- # The columns that have been updated. This isn't completely accurate,
- # see Model#[]=.
- attr_reader :changed_columns
-
# The hash of attribute values. Keys are symbols with the names of the
# underlying database columns.
attr_reader :values
@@ -32,18 +23,16 @@ class Model
# string keys will work if from_db is false.
# * from_db - should only be set by Model.load, forget it
# exists.
- def initialize(values = {}, from_db = false, &block)
- @associations = {}
- @changed_columns = []
+ def initialize(values = {}, from_db = false)
if from_db
@new = false
@values = values
else
@values = {}
@new = true
set(values)
- @changed_columns.clear
- yield self if block
+ changed_columns.clear
+ yield self if block_given?
end
after_initialize
end
@@ -61,7 +50,7 @@ def []=(column, value)
# If the column isn't in @values, we can't assume it is
# NULL in the database, so assume it has changed.
if new? || !@values.include?(column) || value != @values[column]
- @changed_columns << column unless @changed_columns.include?(column)
+ changed_columns << column unless changed_columns.include?(column)
@values[column] = typecast_value(column, value)
end
end
@@ -84,6 +73,19 @@ def ===(obj)
# self.class.
alias_method :model, :class
+ # The current cached associations. A hash with the keys being the
+ # association name symbols and the values being the associated object
+ # or nil (many_to_one), or the array of associated objects (*_to_many).
+ def associations
+ @associations ||= {}
+ end
+
+ # The columns that have been updated. This isn't completely accurate,
+ # see Model#[]=.
+ def changed_columns
+ @changed_columns ||= []
+ end
+
# Deletes and returns self. Does not run destroy hooks.
# Look into using destroy instead.
def delete
@@ -171,7 +173,8 @@ def pk_hash
# exists in the database.
def refresh
@values = this.first || raise(Error, "Record not found")
- @associations.clear
+ changed_columns.clear
+ associations.clear
self
end
alias_method :reload, :refresh
@@ -220,12 +223,12 @@ def save!(*columns)
else
return save_failure(:update) if before_update == false
if columns.empty?
- vals = opts[:changed] ? @values.reject{|k,v| !@changed_columns.include?(k)} : @values
+ vals = opts[:changed] ? @values.reject{|k,v| !changed_columns.include?(k)} : @values
this.update(vals)
- @changed_columns = []
+ changed_columns.clear
else # update only the specified columns
- this.update(@values.reject {|k, v| !columns.include?(k)})
- @changed_columns.reject! {|c| columns.include?(c)}
+ this.update(@values.reject{|k, v| !columns.include?(k)})
+ changed_columns.reject!{|c| columns.include?(c)}
end
after_update
end
@@ -237,7 +240,7 @@ def save!(*columns)
# chanaged. If no columns have been changed, returns nil. If unable to
# save, returns false unless raise_on_save_failure is true.
def save_changes
- save(:changed=>true) || false unless @changed_columns.empty?
+ save(:changed=>true) || false unless changed_columns.empty?
end
# Updates the instance with the supplied values with support for virtual
@@ -357,7 +360,7 @@ def add_associated_object(opts, o)
raise(Sequel::Error, 'associated object does not have a primary key') if opts.need_associated_primary_key? && !o.pk
return if run_association_callbacks(opts, :before_add, o) == false
send(opts._add_method, o)
- @associations[opts[:name]].push(o) if @associations.include?(opts[:name])
+ associations[opts[:name]].push(o) if associations.include?(opts[:name])
add_reciprocal_object(opts, o)
run_association_callbacks(opts, :after_add, o)
o
@@ -378,8 +381,8 @@ def add_reciprocal_object(opts, o)
# Load the associated objects using the dataset
def load_associated_objects(opts, reload=false)
name = opts[:name]
- if @associations.include?(name) and !reload
- @associations[name]
+ if associations.include?(name) and !reload
+ associations[name]
else
objs = if opts.returns_array?
send(opts.dataset_method).all
@@ -393,16 +396,16 @@ def load_associated_objects(opts, reload=false)
run_association_callbacks(opts, :after_load, objs)
# Only one_to_many associations should set the reciprocal object
objs.each{|o| add_reciprocal_object(opts, o)} if opts.set_reciprocal_to_self?
- @associations[name] = objs
+ associations[name] = objs
end
end
# Remove all associated objects from the given association
def remove_all_associated_objects(opts)
raise(Sequel::Error, 'model object does not have a primary key') unless pk
send(opts._remove_all_method)
- ret = @associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if @associations.include?(opts[:name])
- @associations[opts[:name]] = []
+ ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
+ associations[opts[:name]] = []
ret
end
@@ -412,7 +415,7 @@ def remove_associated_object(opts, o)
raise(Sequel::Error, 'associated object does not have a primary key') if opts.need_associated_primary_key? && !o.pk
return if run_association_callbacks(opts, :before_remove, o) == false
send(opts._remove_method, o)
- @associations[opts[:name]].delete_if{|x| o === x} if @associations.include?(opts[:name])
+ associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
remove_reciprocal_object(opts, o)
run_association_callbacks(opts, :after_remove, o)
o

0 comments on commit 22fdc42

Please sign in to comment.